mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 13:46:38 +08:00
Compare commits
216 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 123140b762 | |||
| 6bf5023af1 | |||
| 4be7c19798 | |||
| 32e1a07157 | |||
| 2662c65f57 | |||
| 3cfefb4367 | |||
| ee1110b752 | |||
| 9959397934 | |||
| 5f82f72a6f | |||
| 43e8ef154f | |||
| 4dc4c745c8 | |||
| a5257be319 | |||
| 30f36efe5a | |||
| 5a6c5cf72c | |||
| 189916d1db | |||
| 546856594e | |||
| 457b3397fd | |||
| 47ff33c653 | |||
| 1d41b108fc | |||
| b7a101fc60 | |||
| c85d9104ec | |||
| f3cdbdd7d5 | |||
| e93131ab46 | |||
| 161e6c4e86 | |||
| 3dbc7b3045 | |||
| 4a4189705a | |||
| 6aa71a4da8 | |||
| bdc96f6d8e | |||
| 1c1063f448 | |||
| 29fdc378a1 | |||
| bd659d493d | |||
| 6a2a6028c3 | |||
| 6e85f1158b | |||
| e117e314d9 | |||
| fbb0909638 | |||
| 3eecd31868 | |||
| 68d70bbbe8 | |||
| 08ec945e59 | |||
| 4401cb0d66 | |||
| 36ae6247f9 | |||
| 1088086399 | |||
| 2c74d042ed | |||
| 3ec607106d | |||
| f671a96d8c | |||
| fe5cf9021f | |||
| 1be2461716 | |||
| a0e9484e37 | |||
| 4ca6f2957b | |||
| dfd040a9de | |||
| f29292dd81 | |||
| 4566fc1f53 | |||
| 4e58bdd85b | |||
| c009b9e283 | |||
| 4e33e0e521 | |||
| 7252fb6285 | |||
| 2220e45989 | |||
| 6158a487cf | |||
| 9f9c609809 | |||
| e4c6ce9062 | |||
| 81dd44c8fc | |||
| 3825a7f29a | |||
| d21643feed | |||
| 2525664013 | |||
| a850b0a188 | |||
| fd8148c0db | |||
| edd98f4ff0 | |||
| d8f98e218f | |||
| fefe205158 | |||
| d6e7e2baa2 | |||
| cc50cc695e | |||
| e43312d4c6 | |||
| 92df7d5c84 | |||
| 9632b4e3b8 | |||
| 3b979eb5d5 | |||
| 2635a47d29 | |||
| e00d67f2d9 | |||
| 581822d905 | |||
| b5ebfff19b | |||
| eed227b999 | |||
| 8f08a962e6 | |||
| d3ce26414c | |||
| 663da01bda | |||
| df63b0113a | |||
| d09e64ddc6 | |||
| b968043117 | |||
| 31d10195ca | |||
| 76f3428f5d | |||
| 9de33f7064 | |||
| fe13db95c2 | |||
| 6ddd2da2e8 | |||
| 054dc1a8a8 | |||
| 394e3c4855 | |||
| af36676e2e | |||
| 6fa31cafc7 | |||
| a8e8a940a0 | |||
| a092935623 | |||
| 5af13d0709 | |||
| 9a2616dc0e | |||
| c4e9e94117 | |||
| fce2e014e5 | |||
| 7372ac230b | |||
| 77bdb8bf0e | |||
| fd745d33cb | |||
| 65f899d334 | |||
| f034b73a47 | |||
| bd7f008322 | |||
| 2dc7e72621 | |||
| 2d542733f9 | |||
| c677edba06 | |||
| b827baf19f | |||
| 73beedfc09 | |||
| bc1b861841 | |||
| dfb3972b15 | |||
| 330771e7c7 | |||
| 9ded8c71da | |||
| 77ad3ea7e3 | |||
| 95d7045b4a | |||
| d5f46138d5 | |||
| 4196343ad3 | |||
| 78047d1b38 | |||
| c2bd416daf | |||
| 6e5d49c988 | |||
| 9da1ce8456 | |||
| 9f9cbd4ede | |||
| da1409fdac | |||
| 174198c283 | |||
| 796bf1c22f | |||
| 80dd5f8b31 | |||
| 14d41ad807 | |||
| fe2414ead5 | |||
| 649287a775 | |||
| 2514e7edc4 | |||
| 7ab11154e3 | |||
| 97c10b8d0b | |||
| ceae693a20 | |||
| edb356f40e | |||
| 81ba309650 | |||
| 5612403d48 | |||
| 4775e5cb73 | |||
| bc3d9ee285 | |||
| e654441127 | |||
| a987c0d681 | |||
| a85919fd9e | |||
| 4cb8928e4e | |||
| 57616626fd | |||
| 449d0a5c5b | |||
| 1c89db8ffa | |||
| 46f49cc349 | |||
| f365b3d331 | |||
| 4ae6c2718f | |||
| 8894620b92 | |||
| c2fcd2eddf | |||
| ec70794577 | |||
| bcd669722e | |||
| 9975ac90c4 | |||
| 632c455229 | |||
| b60cde02ac | |||
| 21ed214ba9 | |||
| f4a53d6b5f | |||
| cef3694d11 | |||
| 631d32e5d0 | |||
| c74b70b62e | |||
| fa9ecb5690 | |||
| b9cde88bf6 | |||
| f03718ce8c | |||
| 3423175006 | |||
| d619deec96 | |||
| e094f4a3b7 | |||
| 1bff2dadd4 | |||
| 602e7f5e9c | |||
| 9ec3d5b42d | |||
| 28b1305906 | |||
| a80376972c | |||
| 8300d3ec1c | |||
| 290ddd7b51 | |||
| 5d7a4469ea | |||
| 4e339caa9a | |||
| 2a00d21987 | |||
| f086edda3b | |||
| 899b4e6068 | |||
| fa23cad9e9 | |||
| 806863f303 | |||
| ab8e3d4705 | |||
| fe7f7da537 | |||
| 944b98d4d0 | |||
| 32dc7ef68e | |||
| 32762fdf3c | |||
| 79ed8fd6ab | |||
| 4257b6fd5a | |||
| 8dfe31c1c5 | |||
| 37486eb0c9 | |||
| 462deb4820 | |||
| f8509eed26 | |||
| 6e0b6df314 | |||
| 21962db3bf | |||
| b0117b7c84 | |||
| 95d58eb724 | |||
| c856faca50 | |||
| 5a0821274b | |||
| b69bdf838d | |||
| c35eb749c9 | |||
| e3c84c017a | |||
| 112694f860 | |||
| bd69ac51b5 | |||
| 46fb1a2b79 | |||
| c9a532db65 | |||
| be68b581e9 | |||
| 8853933adc | |||
| 048f6e4535 | |||
| 7b9c8996f9 | |||
| 8947bdc8d8 | |||
| baef42f920 | |||
| dd58e0df66 | |||
| bddf641bf1 | |||
| 83a426d3d6 | |||
| 4f698be0a5 |
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: plan
|
||||
description: 项目级技能:规定在开启新方案、新计划或进行任务交接时,必须将计划落库到 docs/plan 文件夹中并使用对应模板。
|
||||
---
|
||||
|
||||
# Plan & Handover Skill
|
||||
|
||||
当你在当前项目中被要求“开启一个新的方案”、“制定开发计划”或者准备“任务交接(Handover)”时,你**必须**遵循本技能的工作流,将计划或方案落库到 `docs/plan/` 目录下。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **什么时候应当创建实现计划?**
|
||||
> * **必须创建的场景**:新功能开发、涉及多组件的重大架构重构、引入新基础设施依赖,以及存在显著设计决策冲突的**中大型、复杂**需求。
|
||||
> * **绝对不要创建的场景**:改个包名、挪个文件、重命名函数、小修小改修复 Bug 等**轻量级、简单的局部重构**。对于此类改动,应当直接完成并运行单元测试通过后交付,禁止制造冗余的计划文档。
|
||||
|
||||
## 执行工作流 (Workflow)
|
||||
|
||||
### 1. 确定计划类型
|
||||
* **新特性/技术实现计划**:如果你要开发新功能或进行重大重构,你需要创建**实现计划**。
|
||||
* **AI 任务交接计划**:如果当前任务尚未完成但需要记录进度留作以后或其他 AI 代理接手,你需要创建**交接计划**。
|
||||
|
||||
### 2. 读取对应模板
|
||||
在创建计划文档前,必须读取对应的模板内容,并严格按照模板的骨架进行填充:
|
||||
* **实现计划模板**:`docs/plan/implementation-plan-template.md`
|
||||
* **接手计划模板**:`docs/plan/handover-plan-template.md`
|
||||
|
||||
### 3. 落库与命名规范
|
||||
在 `docs/plan/` 目录下创建新的 Markdown 文件进行保存:
|
||||
* **实现计划**命名格式:`docs/plan/YYYYMMDD-[feature-name].md` (例如:`20260605-uptime-kuma-sync.md`)
|
||||
* **接手计划**命名格式:`docs/plan/handover-[task-name].md` (例如:`handover-waf-ip-group.md`)
|
||||
|
||||
### 4. 隔离约束 (极其重要)
|
||||
`docs/plan/` 目录下的文档**仅限内部开发和 AI 代理同步使用**。
|
||||
* **绝对禁止**将新创建的 plan 文档加入到项目的官方导航配置(如 `docs/config.ts` 的 `nav` 或 `sidebar` 导航条中)。
|
||||
* **绝对禁止**通过任何方式将其暴露给文档渲染框架(如 VitePress)对外渲染。
|
||||
|
||||
## 后续动作
|
||||
落库完成后,向用户报告计划已生成在 `docs/plan/` 目录下,并列出文档的核心要点或待决策项(如有),等待用户 Review 或批准后即可推进下一步。
|
||||
@@ -0,0 +1,19 @@
|
||||
.git
|
||||
.idea
|
||||
.github
|
||||
anubis-source
|
||||
**/node_modules
|
||||
**/.next
|
||||
**/build
|
||||
**/dist
|
||||
**/.cache
|
||||
**/coverage
|
||||
**/*.db
|
||||
**/*.log
|
||||
tmp
|
||||
logs
|
||||
.DS_Store
|
||||
.env
|
||||
.env.*
|
||||
docker-compose*.yml
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: plan
|
||||
description: 项目级技能:规定在开启新方案、新计划或进行任务交接时,必须将计划落库到 docs/plan 文件夹中并使用对应模板。
|
||||
---
|
||||
|
||||
# Plan & Handover Skill
|
||||
|
||||
当你在当前项目中被要求“开启一个新的方案”、“制定开发计划”或者准备“任务交接(Handover)”时,你**必须**遵循本技能的工作流,将计划或方案落库到 `docs/plan/` 目录下。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **什么时候应当创建实现计划?**
|
||||
> * **必须创建的场景**:新功能开发、涉及多组件的重大架构重构、引入新基础设施依赖,以及存在显著设计决策冲突的**中大型、复杂**需求。
|
||||
> * **绝对不要创建的场景**:改个包名、挪个文件、重命名函数、小修小改修复 Bug 等**轻量级、简单的局部重构**。对于此类改动,应当直接完成并运行单元测试通过后交付,禁止制造冗余的计划文档。
|
||||
|
||||
## 执行工作流 (Workflow)
|
||||
|
||||
### 1. 确定计划类型
|
||||
* **新特性/技术实现计划**:如果你要开发新功能或进行重大重构,你需要创建**实现计划**。
|
||||
* **AI 任务交接计划**:如果当前任务尚未完成但需要记录进度留作以后或其他 AI 代理接手,你需要创建**交接计划**。
|
||||
|
||||
### 2. 读取对应模板
|
||||
在创建计划文档前,必须读取对应的模板内容,并严格按照模板的骨架进行填充:
|
||||
* **实现计划模板**:`docs/plan/implementation-plan-template.md`
|
||||
* **接手计划模板**:`docs/plan/handover-plan-template.md`
|
||||
|
||||
### 3. 落库与命名规范
|
||||
在 `docs/plan/` 目录下创建新的 Markdown 文件进行保存:
|
||||
* **实现计划**命名格式:`docs/plan/YYYYMMDD-[feature-name].md` (例如:`20260605-uptime-kuma-sync.md`)
|
||||
* **接手计划**命名格式:`docs/plan/handover-[task-name].md` (例如:`handover-waf-ip-group.md`)
|
||||
|
||||
### 4. 隔离约束 (极其重要)
|
||||
`docs/plan/` 目录下的文档**仅限内部开发和 AI 代理同步使用**。
|
||||
* **绝对禁止**将新创建的 plan 文档加入到项目的官方导航配置(如 `docs/config.ts` 的 `nav` 或 `sidebar` 导航条中)。
|
||||
* **绝对禁止**通过任何方式将其暴露给文档渲染框架(如 VitePress)对外渲染。
|
||||
|
||||
## 后续动作
|
||||
落库完成后,向用户报告计划已生成在 `docs/plan/` 目录下,并列出文档的核心要点或待决策项(如有),等待用户 Review 或批准后即可推进下一步。
|
||||
@@ -1,122 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve version metadata
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
SHOULD_RUN=true
|
||||
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
|
||||
|
||||
if [[ "${GITHUB_REF}" == refs/heads/main ]] && [[ -n "$POINTED_TAG" ]]; then
|
||||
SHOULD_RUN=false
|
||||
VERSION="$POINTED_TAG"
|
||||
elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
else
|
||||
VERSION="$(git describe --tags)"
|
||||
fi
|
||||
|
||||
echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
if [[ "$VERSION" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then
|
||||
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Set up Node.js
|
||||
if: steps.version.outputs.should_run == 'true'
|
||||
uses: https://github.com/actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Build Frontend
|
||||
if: steps.version.outputs.should_run == 'true'
|
||||
env:
|
||||
CI: ""
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
cd openflare_server/web
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
NEXT_PUBLIC_APP_VERSION="$VERSION" pnpm build
|
||||
|
||||
- name: Set up Go
|
||||
if: steps.version.outputs.should_run == 'true'
|
||||
uses: https://github.com/actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: openflare_server/go.mod
|
||||
|
||||
- name: Build Server Binaries
|
||||
if: steps.version.outputs.should_run == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p dist
|
||||
|
||||
cd openflare_server
|
||||
go mod download
|
||||
|
||||
while read -r GOOS GOARCH ASSET_NAME; do
|
||||
GOOS="$GOOS" GOARCH="$GOARCH" \
|
||||
go build -trimpath -ldflags "-s -w -X 'openflare/common.Version=$VERSION'" -o "../dist/$ASSET_NAME" .
|
||||
done <<'EOF'
|
||||
linux amd64 openflare-server-linux-amd64
|
||||
linux arm64 openflare-server-linux-arm64
|
||||
darwin amd64 openflare-server-darwin-amd64
|
||||
darwin arm64 openflare-server-darwin-arm64
|
||||
windows amd64 openflare-server-windows-amd64.exe
|
||||
EOF
|
||||
|
||||
- name: Build Agent Binaries
|
||||
if: steps.version.outputs.should_run == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
cd openflare_agent
|
||||
go mod download
|
||||
|
||||
while read -r GOOS GOARCH ASSET_NAME; do
|
||||
GOOS="$GOOS" GOARCH="$GOARCH" \
|
||||
go build -trimpath -ldflags "-s -w -X 'openflare-agent/internal/config.AgentVersion=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/agent
|
||||
done <<'EOF'
|
||||
linux amd64 openflare-agent-linux-amd64
|
||||
linux arm64 openflare-agent-linux-arm64
|
||||
darwin amd64 openflare-agent-darwin-amd64
|
||||
darwin arm64 openflare-agent-darwin-arm64
|
||||
windows amd64 openflare-agent-windows-amd64.exe
|
||||
EOF
|
||||
|
||||
- name: Publish Release
|
||||
if: steps.version.outputs.should_run == 'true'
|
||||
uses: https://gitea.com/actions/gitea-release-action@v1
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.version }}
|
||||
name: ${{ steps.version.outputs.version }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
files: |
|
||||
dist/*
|
||||
draft: false
|
||||
prerelease: ${{ steps.version.outputs.is_prerelease == 'true' }}
|
||||
@@ -0,0 +1,96 @@
|
||||
name: Cleanup prerelease tags
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 3 * * *'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve version metadata
|
||||
id: version
|
||||
run: |
|
||||
SHOULD_RUN=true
|
||||
VERSION="all-prerelease-tags"
|
||||
|
||||
echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
if [[ "$VERSION" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then
|
||||
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Delete prerelease, dangling, and unbound releases/tags
|
||||
if: steps.version.outputs.should_run == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Fetch all tags from remote to ensure full synchronization
|
||||
git fetch --tags --force
|
||||
|
||||
# Get all local/remote git tags starting with 'v'
|
||||
mapfile -t GIT_TAGS < <(git tag --list 'v*' | sort -V)
|
||||
|
||||
# Get all GitHub releases (tags associated with releases)
|
||||
mapfile -t GH_RELEASES < <(gh release list --limit 1000 --json tagName --jq '.[].tagName' 2>/dev/null || true)
|
||||
|
||||
# Helper function to check array containment
|
||||
contains_element() {
|
||||
local e match="$1"
|
||||
shift
|
||||
for e; do [[ "$e" == "$match" ]] && return 0; done
|
||||
return 1
|
||||
}
|
||||
|
||||
DELETED_TAGS=0
|
||||
DELETED_RELEASES=0
|
||||
|
||||
echo "=== Phase 1: Checking and cleaning Git tags ==="
|
||||
for TAG in "${GIT_TAGS[@]}"; do
|
||||
if [[ "$TAG" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then
|
||||
# Formal release tag
|
||||
if ! contains_element "$TAG" "${GH_RELEASES[@]}"; then
|
||||
echo "Delete formal tag not bound to any GitHub release: $TAG"
|
||||
git push origin --delete "refs/tags/$TAG" || true
|
||||
git tag -d "$TAG" || true
|
||||
DELETED_TAGS=$((DELETED_TAGS + 1))
|
||||
else
|
||||
echo "Keep formal release tag (bound to release): $TAG"
|
||||
fi
|
||||
else
|
||||
# Prerelease tag
|
||||
if contains_element "$TAG" "${GH_RELEASES[@]}"; then
|
||||
echo "Delete prerelease release: $TAG"
|
||||
gh release delete "$TAG" --yes || true
|
||||
DELETED_RELEASES=$((DELETED_RELEASES + 1))
|
||||
fi
|
||||
|
||||
echo "Delete prerelease tag: $TAG"
|
||||
git push origin --delete "refs/tags/$TAG" || true
|
||||
git tag -d "$TAG" || true
|
||||
DELETED_TAGS=$((DELETED_TAGS + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=== Phase 2: Checking and cleaning dangling GitHub releases ==="
|
||||
for REL_TAG in "${GH_RELEASES[@]}"; do
|
||||
if ! contains_element "$REL_TAG" "${GIT_TAGS[@]}"; then
|
||||
echo "Delete GitHub release not bound to any Git tag: $REL_TAG"
|
||||
gh release delete "$REL_TAG" --yes || true
|
||||
DELETED_RELEASES=$((DELETED_RELEASES + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=== Summary ==="
|
||||
echo "Successfully deleted $DELETED_TAGS tag(s) and $DELETED_RELEASES release(s)."
|
||||
@@ -0,0 +1,187 @@
|
||||
name: Docker image build (Agent)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Image version/tag to publish, for example v1.0.0-beta"
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (${{ matrix.arch }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-24.04
|
||||
- arch: arm64
|
||||
platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set image metadata
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||
run: |
|
||||
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
|
||||
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
|
||||
|
||||
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}-agent" >> "$GITHUB_ENV"
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
elif [[ -n "$INPUT_VERSION" ]]; then
|
||||
VERSION="$INPUT_VERSION"
|
||||
elif [[ -n "$POINTED_TAG" ]]; then
|
||||
VERSION="$POINTED_TAG"
|
||||
else
|
||||
echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: ./openflare-agent/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
VERSION=${{ env.VERSION }}
|
||||
cache-from: type=gha,scope=docker-agent-${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,ignore-error=true,timeout=20m,scope=docker-agent-${{ matrix.arch }}
|
||||
|
||||
- name: Export digest
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/agent-digests
|
||||
touch "/tmp/agent-digests/${DIGEST#sha256:}"
|
||||
env:
|
||||
DIGEST: ${{ steps.build.outputs.digest }}
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: agent-digests-${{ matrix.arch }}
|
||||
path: /tmp/agent-digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v3
|
||||
with:
|
||||
subject-name: ${{ env.IMAGE }}
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
merge:
|
||||
name: Merge multi-arch manifest
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set image metadata
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||
run: |
|
||||
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
|
||||
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
|
||||
|
||||
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}-agent" >> "$GITHUB_ENV"
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
elif [[ -n "$INPUT_VERSION" ]]; then
|
||||
VERSION="$INPUT_VERSION"
|
||||
elif [[ -n "$POINTED_TAG" ]]; then
|
||||
VERSION="$POINTED_TAG"
|
||||
else
|
||||
echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/agent-digests
|
||||
pattern: agent-digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest list
|
||||
working-directory: /tmp/agent-digests
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
references=()
|
||||
for digest in *; do
|
||||
references+=("${IMAGE}@sha256:${digest}")
|
||||
done
|
||||
|
||||
if [ ${#references[@]} -eq 0 ]; then
|
||||
echo "No digests found in /tmp/agent-digests" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${VERSION}" =~ (alpha|beta|rc) ]]; then
|
||||
FLOATING_TAG="beta"
|
||||
else
|
||||
FLOATING_TAG="latest"
|
||||
fi
|
||||
|
||||
docker buildx imagetools create \
|
||||
-t "${IMAGE}:${VERSION}" \
|
||||
-t "${IMAGE}:${FLOATING_TAG}" \
|
||||
"${references[@]}"
|
||||
|
||||
- name: Inspect image
|
||||
run: docker buildx imagetools inspect "${IMAGE}:${VERSION}"
|
||||
@@ -0,0 +1,187 @@
|
||||
name: Docker image build (OpenFlared)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Image version/tag to publish, for example v1.0.0-beta"
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (${{ matrix.arch }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-24.04
|
||||
- arch: arm64
|
||||
platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set image metadata
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||
run: |
|
||||
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
|
||||
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
|
||||
|
||||
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/openflared" >> "$GITHUB_ENV"
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
elif [[ -n "$INPUT_VERSION" ]]; then
|
||||
VERSION="$INPUT_VERSION"
|
||||
elif [[ -n "$POINTED_TAG" ]]; then
|
||||
VERSION="$POINTED_TAG"
|
||||
else
|
||||
echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: ./openflared/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
VERSION=${{ env.VERSION }}
|
||||
cache-from: type=gha,scope=docker-flared-${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,ignore-error=true,timeout=20m,scope=docker-flared-${{ matrix.arch }}
|
||||
|
||||
- name: Export digest
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/flared-digests
|
||||
touch "/tmp/flared-digests/${DIGEST#sha256:}"
|
||||
env:
|
||||
DIGEST: ${{ steps.build.outputs.digest }}
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flared-digests-${{ matrix.arch }}
|
||||
path: /tmp/flared-digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v3
|
||||
with:
|
||||
subject-name: ${{ env.IMAGE }}
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
merge:
|
||||
name: Merge multi-arch manifest
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set image metadata
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||
run: |
|
||||
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
|
||||
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
|
||||
|
||||
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/openflared" >> "$GITHUB_ENV"
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
elif [[ -n "$INPUT_VERSION" ]]; then
|
||||
VERSION="$INPUT_VERSION"
|
||||
elif [[ -n "$POINTED_TAG" ]]; then
|
||||
VERSION="$POINTED_TAG"
|
||||
else
|
||||
echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/flared-digests
|
||||
pattern: flared-digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest list
|
||||
working-directory: /tmp/flared-digests
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
references=()
|
||||
for digest in *; do
|
||||
references+=("${IMAGE}@sha256:${digest}")
|
||||
done
|
||||
|
||||
if [ ${#references[@]} -eq 0 ]; then
|
||||
echo "No digests found in /tmp/flared-digests" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${VERSION}" =~ (alpha|beta|rc) ]]; then
|
||||
FLOATING_TAG="beta"
|
||||
else
|
||||
FLOATING_TAG="latest"
|
||||
fi
|
||||
|
||||
docker buildx imagetools create \
|
||||
-t "${IMAGE}:${VERSION}" \
|
||||
-t "${IMAGE}:${FLOATING_TAG}" \
|
||||
"${references[@]}"
|
||||
|
||||
- name: Inspect image
|
||||
run: docker buildx imagetools inspect "${IMAGE}:${VERSION}"
|
||||
@@ -0,0 +1,187 @@
|
||||
name: Docker image build (Relay)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Image version/tag to publish, for example v1.0.0-beta"
|
||||
required: false
|
||||
type: string
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (${{ matrix.arch }})
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-24.04
|
||||
- arch: arm64
|
||||
platform: linux/arm64
|
||||
runner: ubuntu-24.04-arm
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set image metadata
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||
run: |
|
||||
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
|
||||
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
|
||||
|
||||
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}-relay" >> "$GITHUB_ENV"
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
elif [[ -n "$INPUT_VERSION" ]]; then
|
||||
VERSION="$INPUT_VERSION"
|
||||
elif [[ -n "$POINTED_TAG" ]]; then
|
||||
VERSION="$POINTED_TAG"
|
||||
else
|
||||
echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
file: ./openflare-relay/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
VERSION=${{ env.VERSION }}
|
||||
cache-from: type=gha,scope=docker-relay-${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,ignore-error=true,timeout=20m,scope=docker-relay-${{ matrix.arch }}
|
||||
|
||||
- name: Export digest
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/relay-digests
|
||||
touch "/tmp/relay-digests/${DIGEST#sha256:}"
|
||||
env:
|
||||
DIGEST: ${{ steps.build.outputs.digest }}
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: relay-digests-${{ matrix.arch }}
|
||||
path: /tmp/relay-digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v3
|
||||
with:
|
||||
subject-name: ${{ env.IMAGE }}
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
||||
merge:
|
||||
name: Merge multi-arch manifest
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set image metadata
|
||||
shell: bash
|
||||
env:
|
||||
INPUT_VERSION: ${{ github.event.inputs.version }}
|
||||
run: |
|
||||
POINTED_TAG="$(git tag --points-at HEAD --list 'v*' | sort -V | tail -n1)"
|
||||
INPUT_VERSION="${INPUT_VERSION//[[:space:]]/}"
|
||||
|
||||
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}-relay" >> "$GITHUB_ENV"
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME}"
|
||||
elif [[ -n "$INPUT_VERSION" ]]; then
|
||||
VERSION="$INPUT_VERSION"
|
||||
elif [[ -n "$POINTED_TAG" ]]; then
|
||||
VERSION="$POINTED_TAG"
|
||||
else
|
||||
echo "workflow_dispatch requires an explicit version input when HEAD is not tagged" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/relay-digests
|
||||
pattern: relay-digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest list
|
||||
working-directory: /tmp/relay-digests
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
references=()
|
||||
for digest in *; do
|
||||
references+=("${IMAGE}@sha256:${digest}")
|
||||
done
|
||||
|
||||
if [ ${#references[@]} -eq 0 ]; then
|
||||
echo "No digests found in /tmp/relay-digests" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${VERSION}" =~ (alpha|beta|rc) ]]; then
|
||||
FLOATING_TAG="beta"
|
||||
else
|
||||
FLOATING_TAG="latest"
|
||||
fi
|
||||
|
||||
docker buildx imagetools create \
|
||||
-t "${IMAGE}:${VERSION}" \
|
||||
-t "${IMAGE}:${FLOATING_TAG}" \
|
||||
"${references[@]}"
|
||||
|
||||
- name: Inspect image
|
||||
run: docker buildx imagetools inspect "${IMAGE}:${VERSION}"
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Docker image builds
|
||||
name: Docker image build (Server)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -72,30 +72,30 @@ jobs:
|
||||
|
||||
- name: Build and push
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: ./openflare_server
|
||||
file: ./openflare_server/Dockerfile
|
||||
context: .
|
||||
file: ./openflare-server/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
VERSION=${{ env.VERSION }}
|
||||
cache-from: type=gha,scope=docker-${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,scope=docker-${{ matrix.arch }}
|
||||
cache-from: type=gha,scope=docker-server-${{ matrix.arch }}
|
||||
cache-to: type=gha,mode=max,ignore-error=true,timeout=20m,scope=docker-server-${{ matrix.arch }}
|
||||
|
||||
- name: Export digest
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
touch "/tmp/digests/${DIGEST#sha256:}"
|
||||
mkdir -p /tmp/server-digests
|
||||
touch "/tmp/server-digests/${DIGEST#sha256:}"
|
||||
env:
|
||||
DIGEST: ${{ steps.build.outputs.digest }}
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ matrix.arch }}
|
||||
path: /tmp/digests/*
|
||||
name: server-digests-${{ matrix.arch }}
|
||||
path: /tmp/server-digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
@@ -143,12 +143,12 @@ jobs:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
path: /tmp/server-digests
|
||||
pattern: server-digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Log into registry
|
||||
uses: docker/login-action@v3
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest list
|
||||
working-directory: /tmp/digests
|
||||
working-directory: /tmp/server-digests
|
||||
shell: bash
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
@@ -168,13 +168,19 @@ jobs:
|
||||
done
|
||||
|
||||
if [ ${#references[@]} -eq 0 ]; then
|
||||
echo "No digests found in /tmp/digests" >&2
|
||||
echo "No digests found in /tmp/server-digests" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${VERSION}" =~ (alpha|beta|rc) ]]; then
|
||||
FLOATING_TAG="beta"
|
||||
else
|
||||
FLOATING_TAG="latest"
|
||||
fi
|
||||
|
||||
docker buildx imagetools create \
|
||||
-t "${IMAGE}:${VERSION}" \
|
||||
-t "${IMAGE}:latest" \
|
||||
-t "${IMAGE}:${FLOATING_TAG}" \
|
||||
"${references[@]}"
|
||||
|
||||
- name: Inspect image
|
||||
@@ -1,30 +1,30 @@
|
||||
name: Build GitHub Pages
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
description: 'Reason'
|
||||
required: false
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v2 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly.
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
|
||||
env:
|
||||
CI: ""
|
||||
run: |
|
||||
cd openflare_server/web
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@releases/v3
|
||||
with:
|
||||
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
BRANCH: gh-pages # The branch the action should deploy to.
|
||||
FOLDER: openflare_server/web/build # The folder the action should deploy.
|
||||
name: Build GitHub Pages
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
description: 'Reason'
|
||||
required: false
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v2 # If you're using actions/checkout@v2 you must set persist-credentials to false in most cases for the deployment to work correctly.
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
|
||||
env:
|
||||
CI: ""
|
||||
run: |
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm build
|
||||
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@releases/v3
|
||||
with:
|
||||
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
|
||||
BRANCH: gh-pages # The branch the action should deploy to.
|
||||
FOLDER: openflare-server/web/build # The folder the action should deploy.
|
||||
+337
-206
@@ -1,7 +1,7 @@
|
||||
name: Release
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
name: Release
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
@@ -11,20 +11,20 @@ on:
|
||||
type: string
|
||||
push:
|
||||
tags: ["v*"]
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_run: ${{ steps.version.outputs.should_run }}
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_run: ${{ steps.version.outputs.should_run }}
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Resolve version metadata
|
||||
id: version
|
||||
env:
|
||||
@@ -52,191 +52,322 @@ jobs:
|
||||
fi
|
||||
|
||||
echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT"
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
if [[ "$VERSION" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then
|
||||
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build-frontend:
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Build Frontend
|
||||
env:
|
||||
CI: ""
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
cd openflare_server/web
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
NEXT_PUBLIC_APP_VERSION="$VERSION" pnpm build
|
||||
|
||||
- name: Upload Frontend Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend-build
|
||||
path: openflare_server/web/build
|
||||
retention-days: 1
|
||||
|
||||
build-binaries:
|
||||
needs:
|
||||
- prepare
|
||||
- build-frontend
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
asset_name: openflare-server-linux-amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
asset_name: openflare-server-linux-arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
asset_name: openflare-server-darwin-amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
asset_name: openflare-server-darwin-arm64
|
||||
- goos: windows
|
||||
goarch: amd64
|
||||
asset_name: openflare-server-windows-amd64.exe
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download Frontend Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend-build
|
||||
path: openflare_server/web/build
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: openflare_server/go.mod
|
||||
|
||||
- name: Build Server
|
||||
working-directory: openflare_server
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
ASSET_NAME: ${{ matrix.asset_name }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
go mod download
|
||||
mkdir -p ../dist
|
||||
go build -trimpath -ldflags "-s -w -X 'openflare/common.Version=$VERSION'" -o "../dist/$ASSET_NAME" .
|
||||
|
||||
- name: Upload Binary Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: server-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: dist/${{ matrix.asset_name }}
|
||||
retention-days: 1
|
||||
|
||||
build-agent-binaries:
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
asset_name: openflare-agent-linux-amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
asset_name: openflare-agent-linux-arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
asset_name: openflare-agent-darwin-amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
asset_name: openflare-agent-darwin-arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: openflare_agent/go.mod
|
||||
|
||||
- name: Build Agent
|
||||
working-directory: openflare_agent
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
ASSET_NAME: ${{ matrix.asset_name }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
go mod download
|
||||
mkdir -p ../dist
|
||||
go build -trimpath -ldflags "-s -w -X 'openflare-agent/internal/config.AgentVersion=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/agent
|
||||
|
||||
- name: Upload Agent Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: agent-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: dist/${{ matrix.asset_name }}
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
needs:
|
||||
- prepare
|
||||
- build-binaries
|
||||
- build-agent-binaries
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Server Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "server-*"
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Download Agent Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "agent-*"
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ needs.prepare.outputs.version }}
|
||||
name: ${{ needs.prepare.outputs.version }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
files: dist/*
|
||||
draft: false
|
||||
prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }}
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
if [[ "$VERSION" =~ ^v[0-9]+(\.[0-9]+)*$ ]]; then
|
||||
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
build-frontend:
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Build Frontend
|
||||
env:
|
||||
CI: ""
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
NEXT_PUBLIC_APP_VERSION="$VERSION" pnpm build
|
||||
|
||||
- name: Upload Frontend Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend-build
|
||||
path: openflare-server/web/build
|
||||
retention-days: 1
|
||||
|
||||
build-binaries:
|
||||
needs:
|
||||
- prepare
|
||||
- build-frontend
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
asset_name: openflare-server-linux-amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
asset_name: openflare-server-linux-arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
asset_name: openflare-server-darwin-amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
asset_name: openflare-server-darwin-arm64
|
||||
- goos: windows
|
||||
goarch: amd64
|
||||
asset_name: openflare-server-windows-amd64.exe
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download Frontend Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: frontend-build
|
||||
path: openflare-server/web/build
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build Server
|
||||
working-directory: openflare-server
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
ASSET_NAME: ${{ matrix.asset_name }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
go mod download
|
||||
mkdir -p ../dist
|
||||
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-server/common.Version=$VERSION'" -o "../dist/$ASSET_NAME" .
|
||||
|
||||
- name: Upload Binary Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: server-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: dist/${{ matrix.asset_name }}
|
||||
retention-days: 1
|
||||
|
||||
build-agent-binaries:
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
asset_name: openflare-agent-linux-amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
asset_name: openflare-agent-linux-arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
asset_name: openflare-agent-darwin-amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
asset_name: openflare-agent-darwin-arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build Agent
|
||||
working-directory: openflare-agent
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
ASSET_NAME: ${{ matrix.asset_name }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
go mod download
|
||||
mkdir -p ../dist
|
||||
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-agent/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/agent
|
||||
(cd ../dist && sha256sum "$ASSET_NAME" > "$ASSET_NAME.sha256")
|
||||
|
||||
- name: Upload Agent Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: agent-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: |
|
||||
dist/${{ matrix.asset_name }}
|
||||
dist/${{ matrix.asset_name }}.sha256
|
||||
retention-days: 1
|
||||
|
||||
build-relay-binaries:
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
asset_name: openflare-relay-linux-amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
asset_name: openflare-relay-linux-arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
asset_name: openflare-relay-darwin-amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
asset_name: openflare-relay-darwin-arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build Relay
|
||||
working-directory: openflare-relay
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
ASSET_NAME: ${{ matrix.asset_name }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
go mod download
|
||||
mkdir -p ../dist
|
||||
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-relay/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/relay
|
||||
(cd ../dist && sha256sum "$ASSET_NAME" > "$ASSET_NAME.sha256")
|
||||
|
||||
- name: Upload Relay Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: relay-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: |
|
||||
dist/${{ matrix.asset_name }}
|
||||
dist/${{ matrix.asset_name }}.sha256
|
||||
retention-days: 1
|
||||
|
||||
build-flared-binaries:
|
||||
needs: prepare
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
asset_name: openflared-linux-amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
asset_name: openflared-linux-arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
asset_name: openflared-darwin-amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
asset_name: openflared-darwin-arm64
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Build Flared
|
||||
working-directory: openflared
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
ASSET_NAME: ${{ matrix.asset_name }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
run: |
|
||||
go mod download
|
||||
mkdir -p ../dist
|
||||
go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflared/internal/config.Version=$VERSION'" -o "../dist/$ASSET_NAME" ./cmd/flared
|
||||
(cd ../dist && sha256sum "$ASSET_NAME" > "$ASSET_NAME.sha256")
|
||||
|
||||
- name: Upload Flared Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flared-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: |
|
||||
dist/${{ matrix.asset_name }}
|
||||
dist/${{ matrix.asset_name }}.sha256
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
needs:
|
||||
- prepare
|
||||
- build-binaries
|
||||
- build-agent-binaries
|
||||
- build-relay-binaries
|
||||
- build-flared-binaries
|
||||
if: needs.prepare.outputs.should_run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Server Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "server-*"
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Download Agent Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "agent-*"
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Download Relay Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "relay-*"
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Download Flared Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "flared-*"
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ needs.prepare.outputs.version }}
|
||||
name: ${{ needs.prepare.outputs.version }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
files: dist/*
|
||||
draft: false
|
||||
prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }}
|
||||
body: |
|
||||
查看完整更新日志: https://open-flare.pages.dev/changelog/
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
+6
-1
@@ -46,5 +46,10 @@ go.work.sum
|
||||
.codex-cache
|
||||
/.gomodcache/
|
||||
*.mmdb
|
||||
!openflare_agent/internal/geoipdata/GeoLite2-Country.mmdb
|
||||
|
||||
*-source
|
||||
*-source
|
||||
*-source.*
|
||||
.codex*
|
||||
|
||||
/bin/
|
||||
@@ -1,41 +1,40 @@
|
||||
# AGENTS.md
|
||||
|
||||
本文件是 OpenFlare 的 AI 接手入口,不承载详细设计、规范和计划。接手项目时,先按顺序阅读以下 VitePress 文档源文件:
|
||||
本文件是 OpenFlare 的 AI 接手入口,不承载详细设计、规范和计划。接手项目时,请根据以下分层文档指引进行阅读与开发:
|
||||
|
||||
1. [docs/design/index.md](./docs/design/index.md)
|
||||
作用:理解当前 MVP 的产品范围、系统边界、核心对象和长期约束。
|
||||
### 1. 开发指导规范 (AI & Developer Guidelines)
|
||||
|
||||
2. [docs/design/architecture.md](./docs/design/architecture.md)
|
||||
作用:理解 Server、Agent、OpenResty 与前端的职责边界。
|
||||
* **必须阅读**:
|
||||
* **[docs/guideline/development-constraints.md](docs/guideline/Constraints.md)**:掌握核心后端/Agent/前端分层约束、数据模型规范、数据库迁移升级协议、API 与鉴权设计准则及变更准入与验收标准。
|
||||
* **[docs/guideline/Role.md](./docs/guideline/Role.md)**:通用的 Go 后端开发与高质量编码准则,包括架构、并发、错误处理、安全及工作流程。
|
||||
* **正在进行的开发计划与接手 (Handover & Plans)**:
|
||||
* **[docs/plan/index.md](./docs/plan/index.md)**:查看正在进行的开发实现计划(Implementation Plan)与 AI 代理交接文档(Handover),接手项目时优先检查。
|
||||
|
||||
3. [docs/design/release-model.md](./docs/design/release-model.md)
|
||||
作用:理解配置发布、激活、回滚与 Agent 应用模型。
|
||||
### 2. 系统设计与架构 (Design Docs)
|
||||
|
||||
4. [docs/design/development.md](./docs/design/development.md)
|
||||
作用:理解当前开发规范、阶段原则、分层约束、数据模型边界、API 约定、Agent 约束、前端规范与测试要求。
|
||||
* **[docs/design/index.md](./docs/design/index.md)**:理解产品范围、系统边界、核心对象及长期约束,以及[仓库结构](./docs/design/index.md#仓库结构)。
|
||||
* **[docs/design/architecture.md](./docs/design/architecture.md)**:理解 Server、Agent、OpenResty 与前端的职责边界与网络拓扑。
|
||||
* **[docs/design/agent-design.md](./docs/design/agent-design.md)**:理解 Agent 设计原则、与 Server 交互时序、OpenResty 管控与配置发布回滚模型。
|
||||
|
||||
5. [docs/guide/deployment.md](./docs/guide/deployment.md)
|
||||
作用:理解当前部署方式、Agent 接入、升级、卸载和联调步骤。
|
||||
### 3. 部署与参考手册 (Deployment & References)
|
||||
|
||||
6. [docs/reference/configuration.md](./docs/reference/configuration.md)
|
||||
作用:理解系统启动时支持的环境变量、命令行参数、运行时配置项和 Agent 配置字段。
|
||||
* **[docs/deployment/deployment.md](./docs/deployment/deployment.md)** / **[server.md](./docs/deployment/server.md)** / **[agent.md](./docs/deployment/agent.md)** / **[upgrade.md](./docs/deployment/upgrade.md)**:Server 和 Agent 的单机、Docker 部署配置,接入、升级与维护策略。
|
||||
* **[docs/reference/configuration.md](./docs/reference/configuration.md)** / **[cli.md](./docs/reference/cli.md)**:支持的环境变量、参数、命令行与配置文件参考。
|
||||
|
||||
线上文档入口:https://open-flare.pages.dev
|
||||
---
|
||||
|
||||
## 执行要求
|
||||
## 开发与执行要求
|
||||
|
||||
* 如果实现内容超出 [产品边界](./docs/design/index.md),先修改设计文档,再继续编码。
|
||||
* 如果实现方式违反 [开发约束](./docs/design/development.md),应优先调整方案,而不是绕过规范。
|
||||
* 如果需求与当前阶段原则冲突,优先遵守 [开发约束](./docs/design/development.md) 中的变更准入与验收标准。
|
||||
* 如果任务涉及前端改造或管理端 UI,必须同时遵守 [开发约束](./docs/design/development.md) 中的前端规范。
|
||||
|
||||
## 文档维护要求
|
||||
|
||||
当以下内容发生变化时,应同步更新对应 VitePress 页面:
|
||||
|
||||
* 产品范围或系统边界变化:更新 `docs/design/index.md`
|
||||
* 系统结构、模块职责变化:更新 `docs/design/architecture.md`
|
||||
* 发布、同步、回滚模型变化:更新 `docs/design/release-model.md`
|
||||
* 开发约束、代码规范、接口约定、阶段原则、测试基线变化:更新 `docs/design/development.md`
|
||||
* 产品启动、部署、升级、联调方式变化:更新 `docs/guide/deployment.md` 和 `README.md`
|
||||
* 环境变量、命令行参数、运行时配置、Agent 配置变化:更新 `docs/reference/configuration.md`
|
||||
1. **设计先行**:
|
||||
* 开发新功能或重要特性时,必须在 `docs/design/` 下创建/更新对应的设计文档,理清架构与核心决策。
|
||||
* 新增的设计文档应同步更新至 `docs/design/architecture.md` 及在 `docs/config.ts` 中注册侧边栏路由。
|
||||
* 若实现内容超出产品边界,必须先修改设计文档,再编码实现。
|
||||
2. **遵守约束**:
|
||||
* 必须严格遵循 `docs/guideline/` 下的所有开发准则与开发约束规范,不得绕过任何规范。
|
||||
* 涉及前端改造或管理端 UI 时,必须遵守 `docs/guideline/development-constraints.md` 中的前端规范。
|
||||
3. **开发计划与交接**:
|
||||
* 正在进行的开发计划或 AI 接手交接发生变化时,在 `docs/plan/` 下更新对应的开发计划或接手文档,并使用相应模板初始化。
|
||||
4. **文档与变更日志**:
|
||||
* 当相关内容发生变化时,同步更新对应的**中文文档**(不要同步英文文档)。
|
||||
* 代码或配置变更完成后,必须在 [`docs/changelog/index.md`](./docs/changelog/index.md) 的 `[Unreleased]` 区块补充对应变更条目。
|
||||
* **纯文档变更(如 `docs/` 下的 Markdown 文档、README 等)不需要写入 changelog。**
|
||||
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
<div align="center">
|
||||
|
||||
# OpenFlare
|
||||
|
||||
**[English](./README.en.md) | [📖 中文](./README.md)**
|
||||
|
||||
OpenFlare is an open-source CDN orchestration and edge security platform. It supports reverse proxies, centralized configuration synchronization, secure intranet penetration (Tunnels), dynamic WAF protection, and anti-CC challenges.
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/Rain-kl/OpenFlare?color=brightgreen" alt="license">
|
||||
</a>
|
||||
<a href="https://github.com/Rain-kl/OpenFlare/releases/latest">
|
||||
<img src="https://img.shields.io/github/v/release/Rain-kl/OpenFlare?color=brightgreen&include_prereleases" alt="release">
|
||||
</a>
|
||||
<a href="https://github.com/Rain-kl/OpenFlare/pkgs/container/openflare">
|
||||
<img src="https://img.shields.io/badge/GHCR-ghcr.io%2Frain--kl%2Fopenflare-brightgreen" alt="ghcr">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
> [!WARNING]
|
||||
> After logging in for the first time with the `root` user, make sure to change the default password `123456`.
|
||||
>
|
||||
> The BETA version is a temporary product for the development and testing phase. It may contain unknown issues and should not be used in production environments.
|
||||
|
||||
## Documentation
|
||||
|
||||
**https://open-flare.pages.dev**
|
||||
|
||||
Quick links:
|
||||
|
||||
* [Quick Start](https://open-flare.pages.dev/en/guide/quick-start)
|
||||
* [Deployment Guide](https://open-flare.pages.dev/en/deployment/deployment)
|
||||
* [Configuration Reference](https://open-flare.pages.dev/reference/configuration)
|
||||
* [System Design](https://open-flare.pages.dev/design/)
|
||||
|
||||
## Core Features
|
||||
|
||||
* **Reverse Proxy Management**: Website rules as the aggregation boundary, supporting multi-domain binding and multi-upstream load balancing with unified management of all OpenResty node configurations.
|
||||
* **Immutable Config Version Control**: Full-snapshot publish model based on version numbers (`YYYYMMDD-NNN`), with pre-publish diff preview, a single globally active version, and one-click sub-second rollback.
|
||||
* **Secure Intranet Penetration (Tunnels)**: An open-source alternative to Cloudflare Tunnels. Securely expose local intranet Web services to the public network via Relay and OpenFlared clients — no public IP or open inbound ports required.
|
||||
* **Edge WAF Safety Protection**: Provides global and custom rule groups, supporting manual/automatic/subscription IP groups, MaxMind GeoIP country-level access control, Checksum-based differential IP group sync (no Nginx reload), and custom block responses.
|
||||
* **Anti-CC & Human-Machine Challenge (PoW)**: Built-in high-performance client-side cryptographic Proof of Work challenges (similar to Turnstile) to block and intercept botnets and scrapers at the gateway edge in seconds.
|
||||
* **Pages Static Hosting**: Upload pre-built ZIP packages directly; edge Agents pull and serve them via local OpenResty, with SPA Fallback and built-in API reverse proxy configuration.
|
||||
* **Automated TLS Certificate Management**: Supports dynamic certificate upload, automatic multi-domain certificate matching and binding, and ACME-based automatic issuance and renewal via Let's Encrypt.
|
||||
* **Uptime Kuma Monitoring Sync**: Integrates with Uptime Kuma to automatically sync the monitoring site list using differential updates, providing real-time awareness of node availability and service health.
|
||||
* **SSO Single Sign-On**: Supports GitHub OAuth and standard OIDC protocol for seamless integration with enterprise identity providers.
|
||||
* **Unified Observability**: Aggregates node request metrics, real-time access log details, host/Nginx resource snapshots, health events, and a re-upload buffer for network fluctuations.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Launch Server
|
||||
|
||||
```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-random-string
|
||||
DSN: postgres://openflare:replace-with-strong-password@postgres:5432/openflare?sslmode=disable
|
||||
GIN_MODE: release
|
||||
LOG_LEVEL: info
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Access at: `http://localhost:3000`
|
||||
|
||||
Default credentials:
|
||||
|
||||
* Username: `root`
|
||||
* Password: `123456`
|
||||
|
||||
### 2. Install Agent
|
||||
|
||||
Before installing an Agent, please install OpenResty on the target node first, or use the Agent Docker image with OpenResty built-in.
|
||||
|
||||
You can copy the installation command from **Node Management -> Details -> Node Info -> Node Token & Deployment** in the control panel, or directly use the scripts below:
|
||||
|
||||
#### Docker Deployment
|
||||
|
||||
For Docker deployment, you can directly run the Agent image:
|
||||
|
||||
```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/tcp -p 443:443/udp \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
#### Local Installation
|
||||
|
||||
Using `discovery_token` to register:
|
||||
|
||||
```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 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 installation script defaults to `/opt/openflare-agent`, creates a `openflare-agent.service`, automatically searches for `openresty`, and can be executed repeatedly to reinstall or upgrade the Agent.
|
||||
|
||||
### 3. Uninstall Agent
|
||||
|
||||
To completely uninstall the Agent and clear local data, run:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
The uninstallation script will stop and remove the `openflare-agent.service`, and delete the entire `/opt/openflare-agent` directory. It will not delete the local OpenResty installation.
|
||||
|
||||
### 4. Publish Your First Configuration
|
||||
|
||||
1. Log in to the management panel and add a reverse proxy rule.
|
||||
2. View the preview or change summary before publishing.
|
||||
3. Activate the new version.
|
||||
4. Agents will receive the configuration and apply it via WebSocket notification or subsequent heartbeats.
|
||||
|
||||
The version number format is fixed as `YYYYMMDD-NNN`. Historical versions are immutable, and rollback is achieved by reactivating an older version.
|
||||
|
||||
## UI Preview
|
||||
|
||||
### Dashboard Overview
|
||||
|
||||

|
||||
|
||||
### Node Details
|
||||
|
||||

|
||||
|
||||
### Proxy Configuration
|
||||
|
||||

|
||||
|
||||
## Management Panel & API
|
||||
|
||||
The management panel includes:
|
||||
|
||||
* Reverse Proxy Rules
|
||||
* Configuration Versions
|
||||
* Node Management
|
||||
* Application Records
|
||||
* TLS Certificates
|
||||
* Domain Management
|
||||
* Pages Static Hosting
|
||||
* WAF Rule Groups
|
||||
* Intranet Tunnels
|
||||
* Uptime Kuma Monitoring Sync
|
||||
* SSO Login Configuration
|
||||
* User Management
|
||||
* Settings
|
||||
* Version Updates
|
||||
* PoW Rules
|
||||
|
||||
After logging in to the dashboard, access Swagger UI at: `/swagger/index.html`
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under [Apache License 2.0](./LICENSE).
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://www.star-history.com/?repos=Rain-kl%2FOpenFlare&type=date&legend=bottom-right">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
|
||||
</picture>
|
||||
</a>
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
# OpenFlare
|
||||
|
||||
轻量、自托管的 OpenResty 控制面,用于管理反向代理规则、配置发布、节点同步、TLS 证书与基础可观测能力。
|
||||
**[📖 中文](./README.md) | [English](./README.en.md)**
|
||||
|
||||
OpenFlare 是开源 CDN 编排与边缘安全平台。它支持反向代理、集中式配置同步、内网穿透(Tunnels)、动态 WAF 防护以及防 CC 挑战。
|
||||
|
||||
</div>
|
||||
|
||||
@@ -20,6 +22,8 @@
|
||||
|
||||
> [!WARNING]
|
||||
> 使用 `root` 用户初次登录系统后,务必修改默认密码 `123456`。
|
||||
>
|
||||
> BETA 版本为开发测试阶段的临时产物,可能存在未知问题,请勿在生产环境使用。
|
||||
|
||||
## 文档
|
||||
|
||||
@@ -28,18 +32,21 @@
|
||||
常用入口:
|
||||
|
||||
* [快速开始](https://open-flare.pages.dev/guide/quick-start)
|
||||
* [部署说明](https://open-flare.pages.dev/guide/deployment)
|
||||
* [部署说明](https://open-flare.pages.dev/deployment/deployment)
|
||||
* [配置项参考](https://open-flare.pages.dev/reference/configuration)
|
||||
* [系统设计](https://open-flare.pages.dev/design/)
|
||||
|
||||
## 核心能力
|
||||
|
||||
* 反向代理网站配置与多域名绑定
|
||||
* 配置预览、发布、激活与历史回滚
|
||||
* Agent 自动注册、心跳、同步、校验、reload 与失败回滚
|
||||
* OpenResty 主配置、性能参数、缓存参数与 Lua 资源托管
|
||||
* TLS 证书、域名资产、节点凭证与版本状态管理
|
||||
* 请求聚合、访问分析、资源快照、健康事件与节点详情
|
||||
* **反代配置管理**:以网站规则为聚合边界,支持多域名绑定与多上游负载均衡,统一管理所有 OpenResty 节点的反代配置。
|
||||
* **安全内网穿透(Tunnels)**:开源版的 Cloudflare Tunnels。无须公网 IP 或暴露入向端口,通过 Relay 中继节点与 OpenFlared 客户端安全反向穿透内网 Web 服务至公网。
|
||||
* **边缘 WAF 安全防护**:提供全局与自定义规则组,支持手动/自动/订阅型 IP 组、MaxMind GeoIP 国家级地域准入、IP 组成员 Checksum 差分同步(无需 Nginx 重载)以及自定义拦截响应。
|
||||
* **防 CC 与人机挑战(PoW)**:内置高性能客户端密码学 Proof of Work 挑战(类似 Turnstile),在网关边缘秒级拦截并阻断僵尸网络与爬虫。
|
||||
* **Pages 静态托管**:直接上传预构建 ZIP 包,由边缘 Agent 拉取并通过 OpenResty 本地提供服务,支持 SPA Fallback 与内置 API 反向代理配置。
|
||||
* **TLS 证书自动化**:支持证书动态上传、多域名证书自动匹配绑定,以及通过 ACME 协议向 Let's Encrypt 自动申请与续期证书。
|
||||
* **Uptime Kuma 监控同步**:与 Uptime Kuma 集成,自动差分同步监控站点列表,实时感知节点存活与服务可用状态。
|
||||
* **SSO 单点登录**:支持 GitHub OAuth 与标准 OIDC 协议,无缝接入企业身份提供商实现统一登录。
|
||||
* **统一观测**:聚合节点请求指标、实时访问日志明细、宿主机与 Nginx 资源快照、健康事件以及网络波动补传缓冲。
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -93,7 +100,25 @@ docker compose up -d
|
||||
|
||||
### 2. 安装 Agent
|
||||
|
||||
**注意:** 安装agent前需确保存已经安装了Docker, 虽然支持裸Openresty,但未得到充分验证,可能存在未知问题.
|
||||
安装 Agent 前请先在节点上安装 OpenResty,或改用内置 OpenResty 的 Agent Docker 镜像。
|
||||
|
||||
你可以在控制面板的节点管理->详情->节点信息->节点标识与部署复制安装命令,或直接使用下面的脚本:
|
||||
|
||||
#### Docker 部署
|
||||
|
||||
Docker 部署可直接运行 Agent 镜像:
|
||||
|
||||
```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/tcp -p 443:443/udp \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
#### 本地部署
|
||||
|
||||
使用 `discovery_token` 接入:
|
||||
|
||||
@@ -111,7 +136,7 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
|
||||
--agent-token YOUR_AGENT_TOKEN
|
||||
```
|
||||
|
||||
安装脚本默认写入 `/opt/openflare-agent`,创建 `openflare-agent.service`,并可重复执行以重装或升级 Agent。
|
||||
安装脚本默认写入 `/opt/openflare-agent`,创建 `openflare-agent.service`,自动查找 `openresty`,并可重复执行以重装或升级 Agent。
|
||||
|
||||
### 3. 卸载 Agent
|
||||
|
||||
@@ -121,17 +146,14 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
卸载脚本会先停止并移除 `openflare-agent.service`、删除整个 `/opt/openflare-agent` 目录,然后根据卸载前保存的 `agent.json` 判断 OpenResty 安装方式:
|
||||
|
||||
* Docker 模式:删除对应 OpenResty 容器,并尝试移除镜像
|
||||
* 本机 `openresty_path` 模式:不改动本机 OpenResty,只提示用户手动卸载
|
||||
卸载脚本会先停止并移除 `openflare-agent.service`、删除整个 `/opt/openflare-agent` 目录,不会删除本机 OpenResty。
|
||||
|
||||
### 4. 发布第一份配置
|
||||
|
||||
1. 登录管理端并新增反代规则
|
||||
2. 在发布前查看预览或变更摘要
|
||||
3. 激活新版本
|
||||
4. 等待 Agent 在后续 heartbeat 中拉取并应用配置
|
||||
4. Agent 通过 WebSocket 通知或后续 heartbeat 拉取并应用配置
|
||||
|
||||
版本号格式固定为 `YYYYMMDD-NNN`,历史版本不可变,回滚通过重新激活旧版本完成。
|
||||
|
||||
@@ -160,13 +182,28 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/unin
|
||||
* 应用记录
|
||||
* TLS 证书
|
||||
* 域名管理
|
||||
* Pages 静态托管
|
||||
* WAF 规则组
|
||||
* 内网穿透(Tunnels)
|
||||
* Uptime Kuma 监控同步
|
||||
* SSO 登录配置
|
||||
* 用户管理
|
||||
* 设置
|
||||
* 版本更新
|
||||
* POW 规则
|
||||
* PoW 规则
|
||||
|
||||
登录管理端后,可访问 Swagger UI:`/swagger/index.html`
|
||||
|
||||
## 开源协议
|
||||
|
||||
本项目采用 [Apache License 2.0](./LICENSE) 开源。
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://www.star-history.com/?repos=Rain-kl%2FOpenFlare&type=date&legend=bottom-right">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&theme=dark&legend=top-left" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=Rain-kl/OpenFlare&type=date&legend=top-left" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
services:
|
||||
agent:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: openflare-agent/Dockerfile
|
||||
container_name: openflare-agent
|
||||
restart: unless-stopped
|
||||
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "127.0.0.1:18081:18081"
|
||||
|
||||
volumes:
|
||||
- ./openflare-agent/data/:/data
|
||||
|
||||
environment:
|
||||
OPENFLARE_SERVER_URL: "http://host.docker.internal:3000"
|
||||
OPENFLARE_AGENT_TOKEN: "07800f31d3f181e65d18dca1407d821c"
|
||||
LOG_LEVEL: "debug"
|
||||
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
relay:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: openflare-relay/Dockerfile
|
||||
container_name: openflare-relay
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./openflare-relay/data/:/app/data
|
||||
environment:
|
||||
OPENFLARE_SERVER_URL: http://host.docker.internal:3000
|
||||
OPENFLARE_DISCOVERY_TOKEN: 85464eeb72c49abc430569d6b9c77f78
|
||||
LOG_LEVEL: "debug"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
|
||||
flared:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: openflared/Dockerfile
|
||||
container_name: openflare-flared
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./openflared/data/:/app/data
|
||||
environment:
|
||||
OPENFLARE_SERVER_URL: "http://host.docker.internal:3000"
|
||||
OPENFLARE_TUNNEL_TOKEN: deb0783ac1e264a9d86440169aca0f09
|
||||
|
||||
@@ -7,11 +7,14 @@ export default defineConfig({
|
||||
title: 'OpenFlare',
|
||||
lastUpdated: true,
|
||||
cleanUrls: true,
|
||||
ignoreDeadLinks: true,
|
||||
metaChunk: true,
|
||||
srcExclude: [
|
||||
'zh/**',
|
||||
'components/**',
|
||||
'snippets/**'
|
||||
'snippets/**',
|
||||
'plan/**',
|
||||
'guideline/**'
|
||||
],
|
||||
|
||||
markdown: {
|
||||
|
||||
@@ -0,0 +1,437 @@
|
||||
---
|
||||
sidebar: false
|
||||
---
|
||||
|
||||
# 更新日志
|
||||
|
||||
本文件记录 OpenFlare 每个版本的重要变更。
|
||||
|
||||
格式基于 [Keep a Changelog](http://keepachangelog.com/),版本号遵循 [语义化版本](http://semver.org/)。
|
||||
|
||||
## 重大变更
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 2.3.2 开始使用 JWT_SECRET 环境变量替代 SESSION_SECRET 进行管理端 API 的 JWT 签名密钥管理。SESSION_SECRET 将会在之后的版本中逐步废弃,请务必尽快迁移到 JWT_SECRET。
|
||||
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### 说明
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增密码登录人机验证(基于 Proof-of-Work 和无感浏览器检测的 Cap 验证码防护)
|
||||
- 新增后端 PoW 校验服务,实现 FNV-1a/XORShift PRNG 难题生成、验证及 JWT 难题校验算法
|
||||
- 新增线程安全的内存 TTL 核销缓存,支持高并发与 Single-use 难题令牌防重放
|
||||
- 新增 Gin 拦截中间件与登录路由 `X-Cap-Token` 自动校验,支持从 HTTP 请求头验证并放行
|
||||
- 前端登录页集成 cap-widget 组件,按需加载 CDN 脚本,实现静默 PoW 求解与令牌提交
|
||||
- 管理后台系统设置页“登录与注册开关”中新增“启用登录人机验证”开关,支持热更新全局防护状态
|
||||
- 新增 Agent 交互式安装向导,支持选择本地安装和 Docker 运行模式;未传参数时自动进入交互菜单
|
||||
- 新增 Docker 运行模式的智能环境检查,检测到未安装 Docker 时支持一键在线安装,中国大陆环境支持多镜像源自动测速优选与加速器配置
|
||||
- 新增 Agent 交互式卸载向导,支持选择本地卸载和 Docker 容器卸载模式;未传参数时自动进入交互菜单
|
||||
- 新增 Pages 静态托管使用指南(`pages-usage.md`),讲解 ZIP 上传、SPA Fallback 与 API 代理配置
|
||||
- 新增 Uptime Kuma 监控同步集成指南(`uptime-kuma.md`),说明同步参数与专属标签隔离机制
|
||||
- 完善 WAF IP 组订阅模式使用指南(`waf-usage.md`),补充 JSON 路径提取映射规则与同步参数说明
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复由于前端验证码组件未携带 `scope` 导致的登录验证码校验失败或过期的问题;通过引入路径参数化路由 `/api/cap/:scope/...` 支持在不同流程中隔离与核销特定 scope 的人机验证难题
|
||||
|
||||
### 变更
|
||||
|
||||
- 重构 `install-agent.sh` 安装脚本与 `uninstall-agent.sh` 卸载脚本以兼容交互式导引、非交互式命令行参数及 Docker 部署/卸载参数(`--docker`/`--method docker`)
|
||||
- 重构 Go 包依赖结构为统一模块(Monorepo),模块命名为 `github.com/rain-kl/openflare`
|
||||
- 移除各子目录下独立的 `go.mod`/`go.sum` 文件,统一由根目录 `go.mod` 进行全局依赖管理与依赖版本锁定
|
||||
- 替换全仓库 Go源文件中的内部引用路径,由本地相对路径迁移为标准 GitHub 绝对导入路径
|
||||
- 适配 Docker 镜像构建,所有组件镜像的 Dockerfile 调整为基于根目录的上下文编译
|
||||
- 更新 GitHub release 自动化发布流水线,适配全新 monorepo 包结构与符号信息注入路径
|
||||
- 简化并重构数据库历史迁移校验逻辑,将版本 2 至 6 的中间校验函数合并到基线校验函数 `validateDatabaseSchemaV7` 中,消除冗余代码
|
||||
- 重构数据库历史迁移校验架构,引入基于 GORM 反射解析(`schema.Parse`)的通用自动表结构校验,彻底废弃老版本中大量手动编写的 `HasTable`/`HasColumn` 结构字段存在性检测代码
|
||||
|
||||
---
|
||||
|
||||
## [v2.3.2] - 2026-06-04
|
||||
|
||||
### 说明
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 2.3.2 开始使用 JWT_SECRET 环境变量替代 SESSION_SECRET 进行管理端 API 的 JWT 签名密钥管理。SESSION_SECRET 将会在之后的版本中逐步废弃,请务必尽快迁移到 JWT_SECRET。
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 `JWT_SECRET` 环境变量,专用于管理端 API JWT 签名密钥;生产环境必须显式配置
|
||||
- 新增 VitePress 更新日志页面(`docs/changelog/index.md`),记录所有版本变更历史
|
||||
|
||||
### 变更
|
||||
|
||||
- 管理端 API 鉴权框架迁移至 `gin-jwt`
|
||||
- 认证方式变更为 Headers 认证.
|
||||
- `JWT_SECRET` 优先于 `SESSION_SECRET` 用于 JWT 签名;未配置时回退到 `SESSION_SECRET`,向下兼容
|
||||
- 屏蔽手动升级入口(`/api/update/manual-upload`、`/api/update/manual-upgrade`),前端隐藏对应 UI 组件
|
||||
|
||||
---
|
||||
|
||||
## [v2.3.1] - 2026-06-03
|
||||
|
||||
### 变更
|
||||
|
||||
- 屏蔽手动升级入口,前端隐藏对应 UI 组件
|
||||
- POW 与 WAF 规则合并, 统一逻辑处理
|
||||
|
||||
---
|
||||
|
||||
## [v2.3.0] - 2026-06-03
|
||||
|
||||
### 新增
|
||||
|
||||
- WAF IP 组支持订阅模式,可从远程文本或 JSON 源定时同步
|
||||
- 新增 Pages 静态站点托管,支持 SPA fallback 路由配置
|
||||
- Agent 实现 WebSocket 实时推送,Server 发布配置后立即通知在线 Agent
|
||||
|
||||
### 变更
|
||||
|
||||
- Agent 数据面与 OpenResty 合并为集成镜像部署方式
|
||||
- 访问日志与观测数据支持数据库分片,按 ID 分片替代原有逻辑
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.8] - 2026-06-03
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复多域名部署场景下跨域认证绕过安全漏洞
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.6] - 2026-06-02
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 Uptime Kuma 集成,支持自动同步监控任务
|
||||
- WAF 新增 PoW(工作量证明)防护能力,可配置有效期
|
||||
|
||||
### 变更
|
||||
|
||||
- 内网穿透支持 TunnelRelay 中继节点(frps),新增 OpenFlared 客户端(frpc)
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.5] - 2026-06-02
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 WAF 自动 IP 组,支持基于 Expr 规则定时聚合请求日志更新名单
|
||||
- WAF IP 组黑白名单支持直接引用 IP 组对象
|
||||
|
||||
### 变更
|
||||
|
||||
- WAF 规则组与网站解耦,支持全局规则组和自定义规则组独立管理
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.4] - 2026-06-02
|
||||
|
||||
### 新增
|
||||
|
||||
- WAF 规则组新增拦截返回配置 Tab
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复 WAF 配置发布后部分规则不生效的问题
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.3] - 2026-06-02
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 WAF 安全防护模块,支持 IP 黑白名单和地域拦截规则
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.2] - 2026-06-01
|
||||
|
||||
### 变更
|
||||
|
||||
- 观测数据支持按时间窗口自动清理,新增数据库自动清理调度器
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.1] - 2026-06-01
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复仪表板概览数据压缩与规范化问题
|
||||
|
||||
---
|
||||
|
||||
## [v2.2.0] - 2026-06-01
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 TLS 证书转换为 ACME 托管证书的接口(`/convert-acme`)
|
||||
- 新增 ACME 账号与 DNS 账号管理页面
|
||||
- 支持 Let's Encrypt 自动申请与续期
|
||||
|
||||
---
|
||||
|
||||
## [v2.1.1] - 2026-06-01
|
||||
|
||||
### 变更
|
||||
|
||||
- Agent 架构调整,采用集成镜像方式内置 OpenResty
|
||||
|
||||
---
|
||||
|
||||
## [v2.0.3] - 2026-05-31
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复版本号生成逻辑,确保使用当日最大序列号
|
||||
|
||||
---
|
||||
|
||||
## [v2.0.1] - 2026-05-30
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复 GitHub 登录逻辑异常
|
||||
|
||||
---
|
||||
|
||||
## [v2.0.0] - 2026-05-30
|
||||
|
||||
### 新增
|
||||
|
||||
- 全面重构发布模型,引入配置版本不可变快照机制
|
||||
- 支持配置版本回滚(重新激活旧版本)
|
||||
- 新增 `source_config_json` 与 `support_files` 供 Agent 获取完整配置包
|
||||
- 新增节点专属 Agent Token 与 Discovery Token 双轨鉴权
|
||||
|
||||
### 变更
|
||||
|
||||
- 数据库迁移框架切换至 goose,统一管理版本升级步骤
|
||||
- Agent API 与管理端 API 鉴权完全分离
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.3] - 2026-05-30
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复节点 IP 自动探测逻辑,优先使用公网地址
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.2] - 2026-05-29
|
||||
|
||||
### 变更
|
||||
|
||||
- Agent 心跳超时后自动退回 HTTP 轮询模式
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.1] - 2026-05-29
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复 Agent WebSocket 升级失败时的重连逻辑
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.0] - 2026-05-29
|
||||
|
||||
### 新增
|
||||
|
||||
- Agent 支持 WebSocket 长连接,Server 发布后实时推送配置变更
|
||||
|
||||
---
|
||||
|
||||
## [v1.8.0] - 2026-05-26
|
||||
|
||||
### 新增
|
||||
|
||||
- 支持自定义 DNS 解析器(`OpenRestyResolvers`)
|
||||
- 新增历史配置快照清理功能
|
||||
|
||||
### 变更
|
||||
|
||||
- CORS 配置支持动态源与凭证
|
||||
- 上游统一渲染为命名 `upstream` 并启用 keepalive
|
||||
|
||||
---
|
||||
|
||||
## [v1.7.0] - 2026-05-25
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 ACME 和 DNS 账号管理功能,支持证书申请与续期
|
||||
|
||||
### 变更
|
||||
|
||||
- 移除新用户注册功能
|
||||
- 更新 Go 版本要求至 1.25+
|
||||
|
||||
---
|
||||
|
||||
## [v1.6.1] - 2026-05-13
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复个人设置页无法查看第三方认证源及解绑功能
|
||||
|
||||
---
|
||||
|
||||
## [v1.6.0] - 2026-05-13
|
||||
|
||||
### 新增
|
||||
|
||||
- 支持 OIDC 单点登录(SSO)
|
||||
|
||||
---
|
||||
|
||||
## [v1.5.0] - 2026-04-25
|
||||
|
||||
### 新增
|
||||
|
||||
- 集成 PoW(Anubis)防护,支持有效期配置
|
||||
|
||||
---
|
||||
|
||||
## [v1.4.0] - 2026-04-01
|
||||
|
||||
### 新增
|
||||
|
||||
- 支持域名级别独立绑定 TLS 证书,每个域名可单独选择证书
|
||||
- 新增批量更新配置项接口
|
||||
- 新增 Agent 卸载脚本
|
||||
|
||||
### 变更
|
||||
|
||||
- 禁用新用户自助注册
|
||||
- 默认服务器块新增 HTTPS 握手拒绝支持
|
||||
|
||||
---
|
||||
|
||||
## [v1.3.2] - 2026-03-30
|
||||
|
||||
### 新增
|
||||
|
||||
- 网站配置支持多域名绑定与共享设置
|
||||
- 新增抽屉式规则创建组件
|
||||
|
||||
---
|
||||
|
||||
## [v1.3.1] - 2026-03-20
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增源站管理功能,支持源站创建、更新与删除
|
||||
|
||||
### 变更
|
||||
|
||||
- 重构代理路由页面,优化输入组件与样式
|
||||
|
||||
---
|
||||
|
||||
## [v1.3.0] - 2026-03-19
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增数据库观测数据手动和自动清理策略
|
||||
- 节点访问日志支持数据库分片,按 ID 分片
|
||||
|
||||
### 变更
|
||||
|
||||
- 数据库版本管理与迁移逻辑重构
|
||||
|
||||
---
|
||||
|
||||
## [v1.2.0] - 2026-03-19
|
||||
|
||||
### 新增
|
||||
|
||||
- 支持多上游地址负载均衡
|
||||
- 新增缓存策略配置(路径前缀、精确路径)
|
||||
- 节点健康事件清理功能
|
||||
|
||||
### 变更
|
||||
|
||||
- 上游渲染改为命名 upstream 并启用 keepalive
|
||||
- 更新 HTTPS 配置,启用 reuseport 与 epoll 事件模型
|
||||
|
||||
---
|
||||
|
||||
## [v1.1.2] - 2026-03-18
|
||||
|
||||
### 变更
|
||||
|
||||
- HTTPS 启用 HTTP/2 支持
|
||||
|
||||
---
|
||||
|
||||
## [v1.1.1] - 2026-03-18
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增获取配置版本详情 API
|
||||
|
||||
### 变更
|
||||
|
||||
- 仪表板概览数据结构优化,添加压缩与规范化
|
||||
|
||||
---
|
||||
|
||||
## [v1.1.0] - 2026-03-18
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增应用日志分页查询与清理功能
|
||||
- 新增访问日志 IP 汇总与趋势查询
|
||||
- 新增 OpenResty DNS 解析器指令支持
|
||||
- Docker 部署支持在运行中容器内执行 reload
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复应用结果警告逻辑
|
||||
- Lua 和证书文件管理重构,优化文件同步与清理机制
|
||||
|
||||
---
|
||||
|
||||
## [v1.0.2] - 2026-03-17
|
||||
|
||||
### 新增
|
||||
|
||||
- 支持 PostgreSQL 数据库,添加数据库迁移逻辑
|
||||
- 新增 Docker Compose 配置,支持 PostgreSQL 联动部署
|
||||
|
||||
### 变更
|
||||
|
||||
- 多个管理端 API 请求方法从 PUT/DELETE 统一改为 POST
|
||||
|
||||
---
|
||||
|
||||
## [v1.0.1] - 2026-03-16
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增 `origin_host` 字段,支持覆盖回源请求的 Host 头
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复代理配置中 SSL 服务器名称和主机头覆盖逻辑
|
||||
|
||||
---
|
||||
|
||||
## [v1.0.0] - 2026-03-15
|
||||
|
||||
OpenFlare 首个正式版本发布。
|
||||
|
||||
### 新增
|
||||
|
||||
- 管理端 UI、管理 API、Agent API 基础功能
|
||||
- 反向代理配置管理与 OpenResty 配置渲染
|
||||
- 配置版本发布与 Agent 同步
|
||||
- TLS 证书导入与管理
|
||||
- 节点注册、心跳与状态观测
|
||||
- SQLite 数据库支持
|
||||
+40
-11
@@ -1,4 +1,4 @@
|
||||
import { defineAdditionalConfig, type DefaultTheme } from 'vitepress'
|
||||
import {type DefaultTheme, defineAdditionalConfig} from 'vitepress'
|
||||
|
||||
export default defineAdditionalConfig({
|
||||
description:
|
||||
@@ -10,7 +10,9 @@ export default defineAdditionalConfig({
|
||||
sidebar: {
|
||||
'/guide/': { base: '/guide/', items: sidebarGuide() },
|
||||
'/reference/': { base: '/reference/', items: sidebarReference() },
|
||||
'/design/': { base: '/design/', items: sidebarDesign() }
|
||||
'/deployment/': { base: '/deployment/', items: sidebarDeployment() },
|
||||
'/design/': { base: '/design/', items: sidebarDesign() },
|
||||
'/changelog/': { base: '/changelog/', items: [] }
|
||||
},
|
||||
|
||||
editLink: {
|
||||
@@ -56,8 +58,10 @@ export default defineAdditionalConfig({
|
||||
function nav(): DefaultTheme.NavItem[] {
|
||||
return [
|
||||
{ text: '指南', link: '/guide/', activeMatch: '/guide/' },
|
||||
{ text: '部署', link: '/deployment/', activeMatch: '/deployment/' },
|
||||
{ text: '参考', link: '/reference/', activeMatch: '/reference/' },
|
||||
{ text: '设计', link: '/design/', activeMatch: '/design/' }
|
||||
{ text: '设计', link: '/design/', activeMatch: '/design/' },
|
||||
{ text: '更新日志', link: '/changelog/', activeMatch: '/changelog/' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -68,12 +72,16 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
|
||||
items: [
|
||||
{ text: '概览', link: '' },
|
||||
{ text: '快速开始', link: 'quick-start' },
|
||||
{ text: '部署说明', link: 'deployment' },
|
||||
{ text: '新建反代配置', link: 'proxy-config' },
|
||||
{ text: 'Pages 静态托管使用', link: 'pages-usage' },
|
||||
{ text: '内网穿透与隧道使用', link: 'tunnel-usage' },
|
||||
{ text: 'WAF 安全防护使用', link: 'waf-usage' },
|
||||
{ text: 'WAF 自动 IP 组语法', link: 'waf-ip-group-expr' },
|
||||
{ text: 'Uptime Kuma 监控同步', link: 'uptime-kuma' },
|
||||
{ text: 'SSO 登录配置', link: 'sso' },
|
||||
{ text: '启动 Server', link: 'server' },
|
||||
{ text: '接入 Agent', link: 'agent' },
|
||||
{ text: '发布第一份配置', link: 'first-site' },
|
||||
{ text: '升级与维护', link: 'upgrade' }
|
||||
{ text: '故障排查', link: 'troubleshooting' },
|
||||
{ text: '引用与致谢', link: 'credits' }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -87,8 +95,24 @@ function sidebarReference(): DefaultTheme.SidebarItem[] {
|
||||
{ text: '概览', link: '' },
|
||||
{ text: '配置项', link: 'configuration' },
|
||||
{ text: '命令与脚本', link: 'cli' },
|
||||
{ text: 'API 约定', link: 'api' },
|
||||
{ text: '仓库结构', link: 'repository' }
|
||||
{ text: 'API 约定', link: 'api' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function sidebarDeployment(): DefaultTheme.SidebarItem[] {
|
||||
return [
|
||||
{
|
||||
text: '部署',
|
||||
items: [
|
||||
{ text: '概览', link: '' },
|
||||
{ text: '部署说明', link: 'deployment' },
|
||||
{ text: '启动 Server', link: 'server' },
|
||||
{ text: '接入 Agent', link: 'agent' },
|
||||
{ text: '部署 Relay (Tunnel)', link: 'relay' },
|
||||
{ text: '部署 OpenFlared', link: 'openflared' },
|
||||
{ text: '升级与维护', link: 'upgrade' }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -101,9 +125,14 @@ function sidebarDesign(): DefaultTheme.SidebarItem[] {
|
||||
items: [
|
||||
{ text: '产品边界', link: '' },
|
||||
{ text: '系统架构', link: 'architecture' },
|
||||
{ text: '发布模型', link: 'release-model' },
|
||||
{ text: '开发约束', link: 'development' }
|
||||
{ text: 'Agent 与发布模型', link: 'agent-design' },
|
||||
{ text: '内网穿透隧道设计', link: 'tunnel-design' },
|
||||
{ text: 'WAF 设计', link: 'waf-design' },
|
||||
{ text: 'Pages 静态托管设计', link: 'pages-design' },
|
||||
{ text: 'Uptime Kuma 监控同步设计', link: 'kuma-design' },
|
||||
{ text: '登录验证码设计', link: 'login-captcha' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
# 接入 Agent
|
||||
|
||||
你会学到:Agent 的职责、两种接入 Token 的区别、安装脚本参数、`agent.json` 配置方式,以及如何确认节点已经上线。
|
||||
|
||||
OpenFlare Agent 运行在代理节点侧。它不会接收远程 shell 指令,而是通过 Agent API 拉取控制面发布的配置版本,在本地写入 OpenResty 文件、执行配置校验、reload,并在失败时尝试回滚到可运行配置。
|
||||
|
||||
## 接入方式
|
||||
|
||||
| 方式 | 适用场景 |
|
||||
| --- | --- |
|
||||
| `discovery_token` | 首次自动注册节点,由 Server 置换为节点专属凭证 |
|
||||
| `agent_token` | 已在管理端创建或分配节点,直接使用节点专属凭证接入 |
|
||||
|
||||
`agent_token` 与 `discovery_token` 至少填写一个。
|
||||
|
||||
### 凭证获取路径
|
||||
|
||||
- **`discovery_token`(自动注册凭证)**:登录管理端后台,导航至「系统设置」->「自动注册」,在页面中可直接生成、查看和复制全局的自动注册凭证。
|
||||
- **`agent_token`(节点专属凭证)**:登录管理端后台,导航至「节点管理」->「新增节点」,填写节点基本信息保存后,在节点详情页面即可直接复制该节点专属的接入 Token。
|
||||
|
||||
## 一键安装
|
||||
|
||||
### 交互式安装 (推荐)
|
||||
|
||||
如果在不传递任何参数的情况下运行安装脚本,脚本将进入交互模式。您将可以通过向导选择安装方式(本地运行 / Docker 容器运行),并配置 Server 地址与认证 Token(若选择 Docker 方式且本地没有 Docker,脚本还会询问并智能安装 Docker):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/install-agent.sh | bash
|
||||
```
|
||||
|
||||
### 自动化 (非交互式) 安装
|
||||
|
||||
如果在执行脚本时附加了任何参数,脚本将进入自动化安装模式,不需要任何交互。
|
||||
|
||||
使用 `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
|
||||
```
|
||||
|
||||
使用节点专属 `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
|
||||
```
|
||||
|
||||
使用 Docker 容器自动化安装:
|
||||
|
||||
```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 \
|
||||
--docker
|
||||
```
|
||||
|
||||
安装脚本在本地安装模式下会下载最新 Agent,默认写入 `/opt/openflare-agent`,生成 `agent.json`,并在 Linux + systemd 环境创建 `openflare-agent.service`。
|
||||
|
||||
支持参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `--server-url` | Server 地址 |
|
||||
| `--discovery-token` | 首次自动注册 Token |
|
||||
| `--agent-token` | 节点专属 Token |
|
||||
| `--install-dir` | 安装目录,默认 `/opt/openflare-agent`(仅本地安装生效) |
|
||||
| `--openresty-path` | OpenResty 二进制路径,未传时自动查找 `openresty`(仅本地安装生效) |
|
||||
| `--repo` | 下载 Agent 的 GitHub 仓库,默认 `Rain-kl/OpenFlare` |
|
||||
| `--no-service` | 不创建 systemd 服务(仅本地安装生效) |
|
||||
| `--docker` | 使用 Docker 容器方式安装 |
|
||||
| `--method` | 安装方式,可选 `local` 或 `docker`(默认 `local`) |
|
||||
|
||||
## 配置文件
|
||||
|
||||
默认配置文件路径:
|
||||
|
||||
```text
|
||||
/opt/openflare-agent/agent.json
|
||||
```
|
||||
|
||||
本地配置示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_path": "openresty",
|
||||
"openresty_observability_port": 18081,
|
||||
"observability_replay_minutes": 15,
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
自定义 OpenResty 路径示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "/var/lib/openflare-agent",
|
||||
"openresty_path": "/usr/local/openresty/nginx/sbin/openresty",
|
||||
"main_config_path": "/var/lib/openflare-agent/etc/nginx/nginx.conf",
|
||||
"route_config_path": "/var/lib/openflare-agent/etc/nginx/conf.d/openflare_routes.conf",
|
||||
"access_log_path": "/var/lib/openflare-agent/var/log/openflare/access.log",
|
||||
"cert_dir": "/var/lib/openflare-agent/etc/nginx/certs",
|
||||
"lua_dir": "/var/lib/openflare-agent/etc/nginx/lua",
|
||||
"runtime_config_dir": "/var/lib/openflare-agent/etc/openflare",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
如果不配置 `openresty_path`,Agent 默认调用 `openresty`。完整字段见 [配置项参考](../reference/configuration.md#agent-配置字段)。
|
||||
|
||||
## Docker 运行
|
||||
|
||||
Docker 部署时直接运行内置 OpenResty 的 Agent 镜像:
|
||||
|
||||
```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/tcp -p 443:443/udp \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
## 启动与验证
|
||||
|
||||
systemd 环境:
|
||||
|
||||
```bash
|
||||
systemctl status openflare-agent
|
||||
journalctl -u openflare-agent -f
|
||||
```
|
||||
|
||||
手动启动:
|
||||
|
||||
```bash
|
||||
/opt/openflare-agent/openflare-agent -config /opt/openflare-agent/agent.json
|
||||
```
|
||||
|
||||
源码运行:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
export LOG_LEVEL='info'
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
编译后二进制运行:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
在管理端确认:
|
||||
|
||||
| 位置 | 期望结果 |
|
||||
| --- | --- |
|
||||
| 节点列表 | 节点在线 |
|
||||
| 节点详情 | 能看到心跳时间、当前版本和基础资源信息 |
|
||||
| 应用记录 | 发布配置后出现应用结果 |
|
||||
|
||||
## 卸载
|
||||
|
||||
### 交互式卸载 (推荐)
|
||||
|
||||
如果在不传递任何参数的情况下运行卸载脚本,脚本将进入交互模式。您可以通过提示菜单选择卸载方式(本地卸载 / Docker 容器卸载):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
### 自动化 (非交互式) 卸载
|
||||
|
||||
使用命令行传参进行无人值守卸载。
|
||||
|
||||
本地卸载(默认):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash -s -- --install-dir /opt/openflare-agent
|
||||
```
|
||||
|
||||
Docker 容器卸载:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash -s -- --docker
|
||||
```
|
||||
|
||||
支持参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `--install-dir` | 安装目录,默认 `/opt/openflare-agent`(仅本地卸载生效) |
|
||||
| `--service-name` | systemd 服务名,默认 `openflare-agent`(仅本地卸载生效) |
|
||||
| `--docker` | 使用 Docker 容器方式卸载 |
|
||||
| `--method` | 卸载方式,可选 `local` 或 `docker`(默认 `local`) |
|
||||
|
||||
本地卸载只会移除 Agent 服务、进程和安装目录,不会删除本机 OpenResty。Docker 卸载会停止并删除 `openflare-agent` 容器,交互模式下还可以选择是否清理对应的 Docker 镜像。
|
||||
|
||||
## 常见问题
|
||||
|
||||
| 现象 | 处理步骤 |
|
||||
| --- | --- |
|
||||
| `agent_token 和 discovery_token 不能同时为空` | 检查 `agent.json` 至少配置了一个 Token |
|
||||
| 节点一直离线 | 在 Agent 节点执行 `curl -I http://your-server:3000`,确认 Server 地址可达 |
|
||||
| OpenResty 没有启动 | 查看 `journalctl -u openflare-agent`,确认 `openresty_path` 可执行且 80/443 端口未被占用 |
|
||||
| 发布后重复失败 | Agent 会阻断同一 `version + checksum` 的重复应用;需要修正配置后重新发布,或激活旧版本回滚 |
|
||||
@@ -0,0 +1,287 @@
|
||||
# 部署说明
|
||||
|
||||
你会学到:OpenFlare 的推荐部署方式、Server 与 Agent 的运行要求、源码启动方式、联调步骤、升级与卸载入口。
|
||||
|
||||
生产环境建议使用 PostgreSQL 作为 Server 数据库,并为 Server 显式配置 `JWT_SECRET`。Agent 部署方式推荐为 Docker 部署(即直接使用内置 OpenResty 的 Agent 镜像);亦支持通过安装脚本或手动本地运行。
|
||||
|
||||
## 部署拓扑
|
||||
|
||||
### 标准反代流量路径
|
||||
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
v
|
||||
OpenFlare Server :3000
|
||||
|
|
||||
| Agent API / heartbeat / config pull
|
||||
v
|
||||
OpenFlare Agent
|
||||
|
|
||||
v
|
||||
OpenResty binary
|
||||
|
|
||||
v
|
||||
Origin service
|
||||
```
|
||||
|
||||
### 内网穿透流量路径
|
||||
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
v
|
||||
OpenResty (Agent, WAF/HTTPS 终结) <-- TunnelRelay 节点
|
||||
|
|
||||
| proxy_pass (127.0.0.1:{vhost_port})
|
||||
v
|
||||
OpenFlareRelay (frps 进程) <-- TunnelRelay 节点
|
||||
|
|
||||
| frp 隧道协议
|
||||
v
|
||||
OpenFlared (frpc 客户端) <-- 内网服务器
|
||||
|
|
||||
v
|
||||
Internal Service (192.168.x.x)
|
||||
```
|
||||
|
||||
## 前置条件
|
||||
|
||||
Server:
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| Go | `1.25+`,仅源码运行需要 |
|
||||
| Node.js | `18+`,仅源码构建管理端需要 |
|
||||
| 数据库 | 可写 SQLite 文件目录,或可访问的 PostgreSQL 实例 |
|
||||
| 端口 | 默认监听 `3000` |
|
||||
|
||||
Agent:
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| 系统 | 安装脚本支持 Linux 和 macOS;systemd 服务仅在 Linux + systemd 环境创建 |
|
||||
| 架构 | `amd64` 或 `arm64` |
|
||||
| OpenResty | 本地部署需要可执行 `openresty`,或通过 `--openresty-path` 指定路径 |
|
||||
| Docker | 仅 Docker 部署 Agent 镜像时需要 |
|
||||
| 网络 | Agent 节点必须能访问 Server 地址 |
|
||||
| GeoIP | WAF 地域规则使用 Agent 本地 MaxMind mmdb;Agent 内置初始库并会定期更新 |
|
||||
|
||||
### 硬件配置推荐
|
||||
|
||||
| 组件 | 最低硬件配额 | 推荐硬件配额 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| **Server 控制面** | 1 核 CPU / 1 GB 内存 / 10 GB 磁盘 | 2 核 CPU / 4 GB 内存 / 50 GB+ 磁盘 | 磁盘用量需根据访问日志留存时长与并发流量合理扩容 |
|
||||
| **Agent 数据面** | 1 核 CPU / 512 MB 内存 / 2 GB 磁盘 | 2 核 CPU / 2 GB 内存 / 10 GB+ 磁盘 | 根据 OpenResty 的并发代理连接量与 WAF 拦截处理扩容 |
|
||||
| **Relay 中继节点**| 1 核 CPU / 1 GB 内存 / 5 GB 磁盘 | 2 核 CPU / 2 GB 内存 / 20 GB 磁盘 | frps 传输中继吞吐量主要受带宽与 CPU 吞吐能力限制 |
|
||||
| **OpenFlared 客户端**| 1 核 CPU / 256 MB 内存 / 1 GB 磁盘 | 1 核 CPU / 512 MB 内存 / 5 GB 磁盘 | 独立运行于内网,自身资源占用极小,保障网络吞吐即可 |
|
||||
|
||||
## Docker Compose 部署 Server
|
||||
|
||||
创建 `docker-compose.yml`:
|
||||
|
||||
```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
|
||||
container_name: openflare
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
JWT_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:
|
||||
```
|
||||
|
||||
启动:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose ps
|
||||
docker compose logs -f openflare
|
||||
```
|
||||
|
||||
首次访问 `http://localhost:3000`,默认账号为 `root` / `123456`。登录后请立即修改默认密码。
|
||||
|
||||
## 源码启动 Server
|
||||
|
||||
先构建管理端前端:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
再启动 Server:
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export JWT_SECRET='replace-with-a-long-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
# 可选:设置后优先使用 PostgreSQL。
|
||||
# export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
go run .
|
||||
```
|
||||
|
||||
默认监听 `3000` 端口。也可以显式指定:
|
||||
|
||||
```bash
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
## Docker 运行 Agent(推荐)
|
||||
|
||||
Docker 部署是 Agent 推荐的部署方式。Docker 部署时直接运行 Agent 镜像,该镜像基于 OpenResty 镜像制作,内置 Agent 控制器与 OpenResty 二进制。未显式配置 `node_ip` 时,Agent 会优先通过第三方 API 获取真实出口 IP,避免把 Docker 网桥地址登记为节点 IP。
|
||||
|
||||
挂载配置文件:
|
||||
|
||||
```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/tcp -p 443:443/udp \
|
||||
-v openflare-agent-data:/data \
|
||||
-v ./agent.json:/etc/openflare/agent.json:ro \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
使用环境变量:
|
||||
|
||||
```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/tcp -p 443:443/udp \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
## Agent 接入(脚本安装)
|
||||
|
||||
除了 Docker 部署外,也支持通过安装脚本将 Agent 部署在本地宿主机上。
|
||||
|
||||
使用 `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
|
||||
```
|
||||
|
||||
使用节点专属 `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
|
||||
```
|
||||
|
||||
安装脚本支持参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `--server-url` | Server 地址,必填 |
|
||||
| `--discovery-token` | 首次自动注册 Token,与 `--agent-token` 二选一 |
|
||||
| `--agent-token` | 节点专属 Token,与 `--discovery-token` 二选一 |
|
||||
| `--install-dir` | 安装目录,默认 `/opt/openflare-agent` |
|
||||
| `--openresty-path` | OpenResty 二进制路径,未传时自动查找 `openresty` |
|
||||
| `--repo` | 下载 Agent 的 GitHub 仓库,默认 `Rain-kl/OpenFlare` |
|
||||
| `--no-service` | 不创建 systemd 服务 |
|
||||
|
||||
确认状态:
|
||||
|
||||
```bash
|
||||
systemctl status openflare-agent
|
||||
journalctl -u openflare-agent -f
|
||||
```
|
||||
|
||||
## 手动运行 Agent
|
||||
|
||||
源码运行:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
export LOG_LEVEL='info'
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
编译后二进制运行:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
最小 `agent.json` 示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_path": "openresty",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
未配置 `openresty_path` 时,Agent 默认调用 `openresty`。
|
||||
|
||||
默认情况下,Agent 在 HTTP 心跳成功后会尝试升级为 WebSocket。升级成功时,Server 发布或激活配置会立即通知 Agent;如果 WebSocket 无法建立或意外断开,Agent 会自动退回 HTTP 心跳同步。
|
||||
|
||||
WAF 地域规则依赖 Agent 本地 `GeoLite2-Country.mmdb`。Agent 启动时会在 `data_dir/etc/openflare/GeoLite2-Country.mmdb` 初始化内置数据库,并按配置周期尝试更新;更新失败只记录警告,不影响配置同步与 OpenResty reload。
|
||||
|
||||
## 升级与卸载
|
||||
|
||||
Server:
|
||||
|
||||
* Root 用户可在管理端顶栏检查并升级正式版。
|
||||
* 如需尝试 preview 版本,可手动检查对应发布。
|
||||
* 也可通过上传 Server 二进制的方式执行确认升级。
|
||||
|
||||
Agent:
|
||||
|
||||
* Agent 默认只跟随正式版自动更新。
|
||||
* Agent 自更新会要求 GitHub Release 同时包含目标二进制和同名 `.sha256` 校验文件,下载后必须通过 SHA-256 校验才会替换本地可执行文件。
|
||||
* 安装脚本可重复执行,用于重装或升级 Agent。
|
||||
* preview 升级需要手动触发。
|
||||
|
||||
卸载 Agent:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
卸载脚本会停止 Agent、删除 systemd 服务和安装目录,不会删除本机 OpenResty。
|
||||
@@ -0,0 +1,24 @@
|
||||
# 部署与升级
|
||||
|
||||
本分区提供 OpenFlare Server、Agent、Relay 中继以及 OpenFlared 内网穿透客户端的详细部署指南、配置说明和升级维护步骤。
|
||||
|
||||
## 内容导航
|
||||
|
||||
### 快速开始
|
||||
* **[快速开始](../guide/quick-start.md)**:5 分钟内使用 Docker Compose 启动 Server 和首个 Agent(推荐新用户)
|
||||
|
||||
### Server 部署
|
||||
* **[启动 Server](./server.md)**:从源码构建前端、启动 Server、选择 SQLite 或 PostgreSQL
|
||||
|
||||
### Agent 部署
|
||||
* **[部署 Agent](./agent.md)**:Agent 接入方式、Docker 部署、脚本安装、配置文件及故障排查
|
||||
|
||||
### Tunnel 内网穿透部署
|
||||
* **[部署 Relay](./relay.md)**:TunnelRelay 节点的配置说明、Docker 部署与宿主机运行指南
|
||||
* **[部署 OpenFlared](./openflared.md)**:内网穿透客户端配置说明、Docker 运行与自同步机制
|
||||
|
||||
### 升级与维护
|
||||
* **[升级与维护](./upgrade.md)**:Server 与 Agent 升级步骤、数据清理策略、验证命令
|
||||
|
||||
### 参考资料
|
||||
* **[部署说明](./deployment.md)**:部署拓扑、前置条件、Docker Compose 配置示例、多种部署方式综览
|
||||
@@ -0,0 +1,119 @@
|
||||
# 部署 OpenFlared 客户端
|
||||
|
||||
你会学到:OpenFlared 客户端的职责、配置参数与环境变量、基于 Docker 运行客户端的方法,以及如何在内网服务器上通过二进制方式独立部署。
|
||||
|
||||
**OpenFlared** 是部署在用户内网(局域网、私有云等无法被公网直接访问的环境)的隧道客户端。它的核心职责是通过 `X-Tunnel-Token` 与控制面(OpenFlare Server)建立通信,并在本地自动拉起并管理一个或多个 **frpc (快速反向代理客户端)** 进程,从而将内网的 HTTP 流量安全、稳定地穿透至外网的中继节点。
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
1. **获取 Tunnel Token**:在 OpenFlare 管理端的「内网穿透」或「隧道管理」页面中,创建一个新的隧道实例,系统会自动生成唯一的 `tunnel_id` 与 `tunnel_token`(形如 `tun-<32hex>`)。
|
||||
2. **网络出方向权限**:内网服务器无需任何公网入方向 IP 或端口映射,但必须能够通过网络访问公网上的 **OpenFlare Server 地址** 以及对应的 **TunnelRelay 节点中继端口 (默认 7000)**。
|
||||
3. **软件依赖**(仅限宿主机直接部署):
|
||||
- 本地需有可执行的 `frpc` 二进制文件(建议版本为 `v0.61.0+` 或最新稳定版 `v0.69.0`),或通过参数显式指定路径。
|
||||
|
||||
---
|
||||
|
||||
## 配置文件与环境变量
|
||||
|
||||
`openflared` 启动时默认会读取当前目录下的 `flared.json`。同时也完全支持通过环境变量进行覆盖。
|
||||
|
||||
### 配置字段详情
|
||||
|
||||
| JSON 字段 | 环境变量 | 说明 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | `OPENFLARE_SERVER_URL` | OpenFlare Server 接口服务地址 | **无(必填)** |
|
||||
| `tunnel_token` | `OPENFLARE_TUNNEL_TOKEN` | 隧道客户端专属认证 Token | **无(必填)** |
|
||||
| `frpc_path` | `OPENFLARE_FRPC_PATH` | frpc 可执行二进制文件路径 | `"frpc"` |
|
||||
| `data_dir` | `OPENFLARE_DATA_DIR` | 本地数据与生成的 `frpc_{relayNodeID}.toml` 存放目录 | `"./data"` |
|
||||
| `state_path` | - | 本地状态记录文件路径(保存最后应用的配置版本)| `"{data_dir}/flared-state.json"` |
|
||||
| `heartbeat_interval`| - | 状态心跳上报周期(支持毫秒数或 Go Duration 字符串) | `10000` (10s) |
|
||||
| `sync_interval` | - | 隧道配置拉取同步周期(支持毫秒数或 Go Duration 字符串) | `30000` (30s) |
|
||||
| `request_timeout` | - | 接口网络请求超时时长 | `10000` (10s) |
|
||||
|
||||
---
|
||||
|
||||
## Docker 运行(推荐)
|
||||
|
||||
Docker 部署是内网运行最简单也最安全的方式。官方的 `openflared` 镜像已经内置了客户端控制器以及 `frpc v0.69.0` 二进制运行时,无需额外搭建环境。
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/rain-kl/openflared:latest
|
||||
docker rm -f openflared 2>/dev/null || true
|
||||
|
||||
docker run -d --name openflared --restart unless-stopped \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_TUNNEL_TOKEN=YOUR_TUNNEL_TOKEN \
|
||||
-v openflared-data:/app/data \
|
||||
ghcr.io/rain-kl/openflared:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 宿主机手动运行
|
||||
|
||||
如果您需要直接在内网的 Linux/macOS/Windows 宿主机上独立运行:
|
||||
|
||||
### 1. 编译二进制
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go build -o flared ./cmd/flared
|
||||
```
|
||||
|
||||
### 2. 准备 `flared.json`
|
||||
|
||||
在程序同级目录下创建 `flared.json` 配置文件:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server-ip:3000",
|
||||
"tunnel_token": "your-tunnel-auth-token",
|
||||
"frpc_path": "/usr/local/bin/frpc",
|
||||
"data_dir": "./data",
|
||||
"heartbeat_interval": "10s",
|
||||
"sync_interval": "30s"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 运行服务
|
||||
|
||||
```bash
|
||||
export LOG_LEVEL='info'
|
||||
./flared -config ./flared.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 启动与验证
|
||||
|
||||
### 1. 自动同步逻辑
|
||||
|
||||
启动成功后,OpenFlared 将执行以下工作流:
|
||||
- **心跳与配置获取**:周期性向 Server 的 `/api/flared/heartbeat` 和 `/api/flared/config` 接口发起同步,验证 Token 并检测配置版本。
|
||||
- **文件渲染**:当检测到配置版本(或校验和 Checksum)变化时,会自动拉取该隧道的完整路由规则。如果绑定了多个中继 Relay,将为每个 Relay 分别在 `data_dir` 下渲染出 `frpc_{relayNodeID}.toml`。
|
||||
- **热重载或重启**:拉起对应的 `frpc` 子进程,或在配置文件发生改变时执行 `frpc reload` / 重启动作,以确保流量映射保持最新。
|
||||
- **异常自恢复**:如果本地 `frpc` 隧道进程异常退出,主控程序会在 5 秒的退避惩罚后自动尝试重新启动。
|
||||
|
||||
### 2. 查看日志与连接状态
|
||||
|
||||
```bash
|
||||
# Docker 容器日志
|
||||
docker logs -f openflared
|
||||
```
|
||||
|
||||
若进程运行无误,您会在日志中看到类似如下输出:
|
||||
```text
|
||||
flared config loaded ...
|
||||
detected frpc version v0.69.0
|
||||
flared process started
|
||||
applying new tunnel config {"version": "...", "checksum": "..."}
|
||||
frpc process missing, starting {"relay_id": "..."}
|
||||
```
|
||||
|
||||
### 3. 管理端确认
|
||||
|
||||
打开管理后台的 **「内网穿透」** 页面:
|
||||
- 查看对应隧道的在线状态,此时应当绿灯显示 **「在线」**。
|
||||
- 您可以清晰地看到该隧道目前连接了哪些中继节点,以及各内网服务的穿透路由详情。
|
||||
@@ -0,0 +1,126 @@
|
||||
# 部署 Relay (Tunnel 中继)
|
||||
|
||||
你会学到:TunnelRelay 节点的职责、`openflare-relay` 的配置项与环境变量、使用 Docker 运行 Relay 的方法,以及如何通过源码手动构建并部署 Relay。
|
||||
|
||||
在 OpenFlare 的内网穿透体系中,**TunnelRelay 节点** 扮演着关键的角色。它与普通的边缘节点(Edge Node)不同,除了运行传统的 Agent(托管 OpenResty 进行 HTTPS/WAF 处理)外,还同机运行了 **Relay (frps 隧道管理器)** 服务,负责监听内网客户端(OpenFlared)的隧道连接并进行流量中继。
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
在部署 TunnelRelay 节点之前,请确保:
|
||||
|
||||
1. **已注册为 TunnelRelay 类型节点**:在 OpenFlare 管理端「节点管理」中,添加一个类型为 `tunnel_relay` 的节点,并获取其专属的 `agent_token` 或使用全局 `discovery_token`。
|
||||
2. **网络端口**:
|
||||
- 必须确保 `bindPort`(frpc 连接端口,默认 `7000`)可被公网/内网客户端访问。
|
||||
- 必须确保 `vhostHTTPPort`(HTTP Vhost 端口,默认 `8080`)处于空闲状态,Agent 将在此端口上与 frps 进行流量传递。
|
||||
3. **软件依赖**(仅限宿主机直接部署):
|
||||
- 本地需有可执行的 `frps` 二进制文件(建议版本为 `v0.61.0+` 或最新稳定版 `v0.69.0`),或通过参数显式指定路径。
|
||||
|
||||
---
|
||||
|
||||
## 配置文件与环境变量
|
||||
|
||||
`openflare-relay` 启动时默认会读取当前目录下的 `relay.json`。同时也完全支持通过环境变量进行覆盖。
|
||||
|
||||
### 配置字段详情
|
||||
|
||||
| JSON 字段 | 环境变量 | 说明 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | `OPENFLARE_SERVER_URL` | OpenFlare Server 接口服务地址 | **无(必填)** |
|
||||
| `agent_token` | `OPENFLARE_AGENT_TOKEN` | 节点专属 Token | 与下者二选一 |
|
||||
| `discovery_token` | `OPENFLARE_DISCOVERY_TOKEN` | 自动注册 Token | 与上者二选一 |
|
||||
| `node_name` | `OPENFLARE_NODE_NAME` | 节点标识名称 | 默认获取本机主机名 |
|
||||
| `node_ip` | `OPENFLARE_NODE_IP` | 节点出口/监听 IP | 自动检测真实出口 IP |
|
||||
| `frps_path` | `OPENFLARE_FRPS_PATH` | frps 可执行二进制文件路径 | `"frps"` |
|
||||
| `data_dir` | `OPENFLARE_DATA_DIR` | 本地数据与生成的 `frps.toml` 存放目录 | `"./data"` |
|
||||
| `state_path` | - | 本地状态 JSON 记录文件路径 | `"{data_dir}/relay-state.json"` |
|
||||
| `heartbeat_interval`| - | 心跳周期(支持毫秒数或 Go Duration 字符串) | `10000` (10s) |
|
||||
| `request_timeout` | - | 接口请求超时时长 | `10000` (10s) |
|
||||
|
||||
---
|
||||
|
||||
## Docker 运行(推荐)
|
||||
|
||||
Docker 运行是 TunnelRelay 节点最便捷的部署方案。官方镜像内置了 `openflare-relay` 控制器与 `frps v0.69.0` 运行时,开箱即用。
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/rain-kl/openflare-relay:latest
|
||||
docker rm -f openflare-relay 2>/dev/null || true
|
||||
|
||||
docker run -d --name openflare-relay --restart unless-stopped \
|
||||
-p 7000:7000 \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
-v openflare-relay-data:/var/lib/openflare-relay \
|
||||
ghcr.io/rain-kl/openflare-relay:latest
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> 这里的 `-p 7000:7000` 映射的是 `frpc` 客户端连接中继的端口。如果管理端配置了自定义的 `relay_bind_port`,请对应修改宿主机端口映射。
|
||||
|
||||
---
|
||||
|
||||
## 宿主机手动运行
|
||||
|
||||
如果您倾向于在物理机或虚拟机上直接运行:
|
||||
|
||||
### 1. 编译二进制
|
||||
|
||||
```bash
|
||||
cd openflare-relay
|
||||
go build -o openflare-relay ./cmd/relay
|
||||
```
|
||||
|
||||
### 2. 准备 `relay.json`
|
||||
|
||||
在程序同级目录下创建 `relay.json` 配置文件:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "your-relay-node-agent-token",
|
||||
"frps_path": "/usr/local/bin/frps",
|
||||
"data_dir": "./data",
|
||||
"heartbeat_interval": "10s",
|
||||
"request_timeout": "10s"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 运行服务
|
||||
|
||||
```bash
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-relay -config ./relay.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 启动与验证
|
||||
|
||||
### 1. 查看进程日志
|
||||
|
||||
```bash
|
||||
# Docker 容器日志
|
||||
docker logs -f openflare-relay
|
||||
```
|
||||
|
||||
如果是在 Linux 上通过 Systemd 托管的,可执行:
|
||||
```bash
|
||||
journalctl -u openflare-relay -f
|
||||
```
|
||||
|
||||
### 2. 验证运行状态
|
||||
|
||||
启动成功后,Relay 将进行以下工作:
|
||||
- 向控制面发送 HTTP 心跳以注册/上线。
|
||||
- 从控制面获取最新的 frps 基础配置(包括 `bindPort`、`vhostHTTPPort` 与自动生成的隧道认证凭证 `auth_token`)。
|
||||
- 在本地自动渲染出 `data/frps.toml` 配置文件。
|
||||
- 自动拉起子进程 `frps -c data/frps.toml`。
|
||||
- 如果进程意外崩溃,Relay 将在 2 秒后自动拉起它。
|
||||
|
||||
### 3. 管理端确认
|
||||
|
||||
登录管理后台,导航至 **「节点管理」**,确认:
|
||||
- 该 TunnelRelay 节点状态标记为 **「在线」**。
|
||||
- 节点类型正确标记为 **中继节点** 且 frps 运行状态为 **正常 (Healthy)**。
|
||||
@@ -0,0 +1,172 @@
|
||||
# 启动 Server
|
||||
|
||||
你会学到:如何从源码构建管理端前端、启动 OpenFlare Server、选择 SQLite 或 PostgreSQL,并访问 Swagger。
|
||||
|
||||
OpenFlare Server 是 Gin + GORM 单体控制面,负责管理端 UI、管理 API、Agent API、配置渲染、版本发布、数据存储与聚合查询。
|
||||
|
||||
## 前置条件
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| Go | `1.25+` |
|
||||
| Node.js | `18+` |
|
||||
| pnpm | 推荐通过 `corepack enable` 使用项目声明的 pnpm |
|
||||
| 数据库 | SQLite 文件目录可写,或可访问的 PostgreSQL 实例 |
|
||||
|
||||
生产环境必须显式配置 `JWT_SECRET`,并优先使用 PostgreSQL。
|
||||
|
||||
## 构建管理端前端
|
||||
|
||||
Go Server 会托管 `openflare-server/web/build` 中的静态产物。源码启动前先构建前端:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
常用前端检查:
|
||||
|
||||
```bash
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
```
|
||||
|
||||
## 使用 SQLite 启动
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export JWT_SECRET='replace-with-a-long-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
go run .
|
||||
```
|
||||
|
||||
默认监听 `3000` 端口,访问:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
## 使用 PostgreSQL 启动
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export JWT_SECRET='replace-with-a-long-random-string'
|
||||
export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
export LOG_LEVEL='info'
|
||||
go run .
|
||||
```
|
||||
|
||||
`DSN` 设置后优先于 SQLite。`DSN` 与兼容旧命名的 `SQL_DSN` 同时存在时,优先使用 `DSN`。
|
||||
|
||||
如果目标 PostgreSQL 数据库为空且本地 `SQLITE_PATH` 文件存在,Server 启动阶段会尝试把 SQLite 数据迁移到 PostgreSQL,并在日志中输出迁移进度。
|
||||
|
||||
## 使用 Docker 启动
|
||||
|
||||
使用 Docker 部署可以免去本地配置 Go 与 Node.js 前端构建环境的麻烦。OpenFlare 官方提供了完整的 Dockerfile 与 Compose 配置,支持独立容器启动及多服务联动部署。
|
||||
|
||||
### 1. 使用 Docker Run 极速启动(以 SQLite 为例)
|
||||
|
||||
确保当前目录下已创建用于持久化数据库和日志的数据卷目录。运行以下命令启动 Server:
|
||||
|
||||
```bash
|
||||
# 创建本地挂载目录
|
||||
mkdir -p ./openflare-data
|
||||
|
||||
# 启动容器
|
||||
docker run -d \
|
||||
--name openflare-server \
|
||||
-p 3000:3000 \
|
||||
-v $(pwd)/openflare-data:/data \
|
||||
-e JWT_SECRET='replace-with-a-long-random-string' \
|
||||
-e SQLITE_PATH='/data/openflare.db' \
|
||||
-e GIN_MODE='release' \
|
||||
-e LOG_LEVEL='info' \
|
||||
openflare-server:latest
|
||||
```
|
||||
|
||||
启动参数说明:
|
||||
* **`-p 3000:3000`**:映射宿主机 `3000` 端口到容器内 `3000` 端口。
|
||||
* **`-v $(pwd)/openflare-data:/data`**:挂载本地目录到容器的 `/data`,确保数据库文件 `openflare.db` 在重启或重建容器时不丢失。
|
||||
* **`JWT_SECRET`**:管理端 API 登录令牌的 JWT 签名密钥,生产环境必须配置,避免重启后已登录令牌全部失效。
|
||||
|
||||
---
|
||||
|
||||
### 2. 使用 Docker Compose 一键启动(集成 PostgreSQL)
|
||||
|
||||
推荐在生产环境使用 Docker Compose,自动编排独立的 PostgreSQL 数据库并建立服务间的高可用关联。
|
||||
|
||||
在项目控制面目录下使用 `docker-compose.yaml` 进行编排:
|
||||
|
||||
```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: openflare-server:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
JWT_SECRET: replace-with-random-string
|
||||
SQLITE_PATH: /data/openflare.db
|
||||
DSN: postgres://openflare:replace-with-strong-password@postgres:5432/openflare?sslmode=disable
|
||||
GIN_MODE: release
|
||||
LOG_LEVEL: info
|
||||
volumes:
|
||||
- ./openflare-data:/data
|
||||
```
|
||||
|
||||
启动命令:
|
||||
|
||||
```bash
|
||||
# 启动编排服务
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Compose 参数说明:
|
||||
* **`depends_on` 与 `healthcheck`**:通过 PostgreSQL 的健康度检查(pg_isready),确保数据库初始化完成并完全准备就绪后,再自动拉起 OpenFlare 控制面服务,避免首次连接数据库失败抛出 panic。
|
||||
* **数据目录分离挂载**:`postgres` 数据挂载在 `./postgres-data`,`openflare` 数据与本地备份挂载在 `./openflare-data`,结构清晰,便于日常备份和维护。
|
||||
|
||||
|
||||
## 命令行参数
|
||||
|
||||
```bash
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
| 参数 | 作用 | 默认值 |
|
||||
| --- | --- | --- |
|
||||
| `--port` | 指定 Server 监听端口 | `3000` |
|
||||
| `--log-dir` | 指定日志目录 | 空,输出到标准输出 |
|
||||
| `--version` | 输出版本后退出 | `false` |
|
||||
| `--help` | 输出帮助后退出 | `false` |
|
||||
|
||||
## 首次登录
|
||||
|
||||
默认账号:
|
||||
|
||||
| 用户名 | 密码 |
|
||||
| --- | --- |
|
||||
| `root` | `123456` |
|
||||
|
||||
首次登录后请立即修改默认密码。
|
||||
@@ -1,11 +1,24 @@
|
||||
# 升级与维护
|
||||
|
||||
你会学到:如何升级 Server 与 Agent、如何清理观测数据,以及维护前后应该执行哪些验证命令。
|
||||
|
||||
升级前建议先确认当前激活版本、最近一次 Agent 应用结果和数据库备份策略。生产环境不要在发布配置、Agent 大规模重连或数据库迁移进行中同时升级。
|
||||
|
||||
## Server 升级
|
||||
|
||||
Root 用户可以在管理端顶栏检查并升级 Server 正式版。也可以通过上传 Server 二进制的方式执行确认升级。
|
||||
|
||||
如需尝试 preview 版本,可手动检查对应发布。生产环境建议优先使用正式版。
|
||||
|
||||
升级后确认:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs -n 100 openflare
|
||||
```
|
||||
|
||||
如果是源码部署,重新启动 Server 后确认日志中没有数据库迁移或启动错误。
|
||||
|
||||
## Agent 升级
|
||||
|
||||
节点 Agent 默认只跟随正式版自动更新。preview 升级需要手动触发。
|
||||
@@ -18,6 +31,15 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
|
||||
--agent-token YOUR_AGENT_TOKEN
|
||||
```
|
||||
|
||||
注意:当前安装脚本重装时会删除整个安装目录,包括旧 `agent.json`、本地状态、缓存数据和下载的二进制。执行前请确认手头仍有可用 Token。
|
||||
|
||||
升级后确认:
|
||||
|
||||
```bash
|
||||
systemctl status openflare-agent
|
||||
journalctl -u openflare-agent -n 100 --no-pager
|
||||
```
|
||||
|
||||
## 数据维护
|
||||
|
||||
管理端设置页可以维护观测数据自动清理策略:
|
||||
@@ -27,27 +49,4 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
|
||||
| `DatabaseAutoCleanupEnabled` | 是否启用每日自动清理 |
|
||||
| `DatabaseAutoCleanupRetentionDays` | 自动清理保留天数,至少 1 天 |
|
||||
|
||||
开启后,Server 会在每天凌晨 3 点清理访问日志、指标快照与请求报告。
|
||||
|
||||
## 常用验证命令
|
||||
|
||||
Server:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
Agent:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
Frontend:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
pnpm build
|
||||
```
|
||||
开启后,Server 会在每天凌晨 3 点清理访问日志、指标快照与请求报告。
|
||||
@@ -0,0 +1,175 @@
|
||||
# Agent 设计文档
|
||||
|
||||
你会学到:Agent 的设计原则、核心功能模块、与 Server 的交互链路,以及如何通过不可变版本模型与三阶段容灾机制来保证配置应用的安全性和可靠性。
|
||||
|
||||
---
|
||||
|
||||
## 需求分析
|
||||
|
||||
在分布式反向代理与边缘安全网关场景中,Agent 扮演着打通控制面(Server)与数据面(OpenResty)的核心角色。由于 Agent 运行在用户实际的节点服务器上,其设计必须遵循以下核心安全与高可用需求:
|
||||
|
||||
1. **主动拉取(Pull 模型)而非被动接收**:Server 不直接持有节点的 SSH 秘钥,也不主动发起向节点的入向连接。所有控制指令与配置更新均由 Agent 主动通过心跳(Heartbeat)或长连接(WebSocket)向上拉取。这消除了节点侧的入向防火墙安全隐患,防止了控制通道被劫持。
|
||||
2. **极低侵入性**:Agent 作为一个独立的 Go 二进制进程运行,只与本地 OpenResty 进程进行基于文件的配置重写与信号通知交互,不干涉节点上的其他系统服务。
|
||||
3. **极强容灾与自愈能力**:由于网络抖动、磁盘写满或异常配置等因素极易导致配置同步失败,Agent 必须具备零依赖的本地回滚自愈能力,严防因单次配置失误导致整机服务彻底瘫痪。
|
||||
4. **纯粹的数据与状态落地**:Agent 仅负责承载 Server 渲染好的文件与控制意图落地,不包含复杂的业务逻辑校验、多端租户鉴权等控制面职责,确保了节点侧的高效与轻量。
|
||||
|
||||
---
|
||||
|
||||
## 核心功能
|
||||
|
||||
Agent 主要由以下核心子模块组成,共同配合完成其完整的生命周期管理:
|
||||
|
||||
| 模块名称 | 对应目录 | 功能职责 |
|
||||
| :--- | :--- | :--- |
|
||||
| **配置同步** | `sync/` | 负责拉取完整配置包,写入文件,触发重载,记录并回报同步状态。 |
|
||||
| **心跳管理** | `heartbeat/` | 定期向 Server 上报节点健康状态、资源指标,并获取最新激活版本摘要。 |
|
||||
| **WebSocket** | `wsclient/` | 保持与 Server 的长连接,提供秒级实时的配置推送与控制面指令响应。 |
|
||||
| **OpenResty 管控** | `nginx/` | 执行 Nginx 配置校验 (`openresty -t`)、重写、平滑重载 (`reload`) 及进程自启动。 |
|
||||
| **本地状态库** | `state/` | 持久化记录本地应用版本、错误日志及未成功上报的可观测性指标缓冲。 |
|
||||
| **自更新服务** | `updater/` | 监听 Server 自更新指令,安全拉取新版本二进制并完成原地热升级。 |
|
||||
| **可观测性** | `observability/` | 采集系统宿主机 CPU/内存/磁盘及 Nginx 性能指标,处理访问日志并上报。 |
|
||||
| **GeoIP 维护** | `geoipdata/` `geoipupdate/` | 维护并定期更新本地 GeoIP 数据库,为 WAF 地域过滤提供支撑。 |
|
||||
|
||||
---
|
||||
|
||||
## 与 Server 的交互链路
|
||||
|
||||
Agent 在生命周期中主要通过 **基于 Token 的自动注册** 和 **心跳/WebSocket 双通道** 与控制面通信。
|
||||
|
||||
### 1. 自动注册流程
|
||||
若 Agent 启动时本地 `agent.json` 的 `access_token` 为空,但配置了 `discovery_token`,将触发自动注册流程:
|
||||
1. Agent 向控制面 `/api/agent/register` 发送注册请求,携带本地硬件摘要、IP 及主机名。
|
||||
2. Server 校验 `discovery_token` 有效后,在数据库生成唯一的 `NodeID` 与专属 `AccessToken`(即 `agent_token`)并返回。
|
||||
3. Agent 将获取的专用 Token 写入本地配置文件,擦除一次性 `discovery_token`,后续所有的通信均基于专属 `AccessToken` 进行鉴权认证。
|
||||
|
||||
### 2. 双通道心跳与同步机制
|
||||
* **HTTP 轮询通道(兜底与探测)**:Agent 默认按设定的 `heartbeat_interval` 间隔发送 POST 心跳包。上报指标的同时获取当前激活版本的摘要信息(Version & Checksum)。
|
||||
* **WebSocket 通道(实时通信)**:在 HTTP 心跳成功后,Agent 自动尝试将连接升级为 WebSocket (`/api/agent/ws`)。
|
||||
* WS 连接建立后,心跳与指标上报全面转移到 WS 管道,降低网络开销。
|
||||
* Server 发布或激活新版本时,通过 WS 广播通知 Agent。Agent 收到变更事件后,**立即触发同步流程**,实现秒级配置生效。
|
||||
* 若 WS 链路因网络问题断开,Agent 自动降级为 HTTP 轮询,并采用指数退避机制尝试重建 WS。
|
||||
|
||||
### 3. 交互时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Agent as OpenFlare Agent
|
||||
participant OR as 本地 OpenResty
|
||||
participant Server as OpenFlare Server
|
||||
|
||||
Note over Agent: 首次启动 (无 AccessToken)
|
||||
Agent->>Server: 1. 自动注册请求 (携带 discovery_token)
|
||||
Server-->>Agent: 2. 颁发 NodeID 与专属 AccessToken (agent_token)
|
||||
Note over Agent: 存储 Token 至本地配置文件
|
||||
|
||||
rect rgb(240, 248, 255)
|
||||
Note over Agent, Server: HTTP 兜底与 WebSocket 升级
|
||||
Agent->>Server: 3. 发送 HTTP Heartbeat (上报系统状态与健康度)
|
||||
Server-->>Agent: 4. 返回 ActiveConfig 摘要及 AgentSettings
|
||||
Agent->>Server: 5. 发起 WebSocket 升级请求 (/api/agent/ws)
|
||||
Server-->>Agent: 6. 升级成功 (建立双向持久实时通道)
|
||||
end
|
||||
|
||||
rect rgb(245, 245, 245)
|
||||
Note over Agent, Server: 实时配置发布应用链路
|
||||
Note over Server: 管理员在 UI 点击发布配置
|
||||
Server->>Agent: 7. 通过 WS 广播新配置摘要 (WSMessageTypeActiveConfig)
|
||||
Agent->>Server: 8. 请求拉取完整配置详情 (携带目标 Version/Checksum)
|
||||
Server-->>Agent: 9. 返回完整配置快照 (Nginx配置、证书、WAF规则等)
|
||||
Note over Agent: 备份旧文件,写入新配置至本地临时路径
|
||||
Agent->>OR: 10. 执行配置语法校验 (openresty -t)
|
||||
OR-->>Agent: 11. 返回语法校验结果 (OK)
|
||||
Agent->>OR: 12. 平滑重载信号 (openresty -s reload)
|
||||
Agent->>Server: 13. 上报应用成功状态 (Apply Log & ActiveVersion)
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OpenResty 的管控
|
||||
|
||||
Agent 对数据面 OpenResty 的管控实现了端到端的闭环,包含配置落地、语法验证、平滑重载和异常状态捕获:
|
||||
|
||||
### 1. 配置文件的落地组织
|
||||
同步成功后,Agent 会将配置按照特定的物理结构写入到本地 `/etc/nginx/openflare-lua/` 目录下(或配置指定的 `LuaDir`):
|
||||
* `nginx.conf`:主配置文件(替换相关占位符,配置性能参数、Shared Dictionaries 及全局 Server)。
|
||||
* `routes.conf`:路由配置文件(由 Agent 生成,包含所有代理网站的 Server 块、证书路径、缓存及速率限制指令)。
|
||||
* `certs/`:证书存放目录(文件命名为 `{cert_id}.crt` 和 `{cert_id}.key`)。
|
||||
* `waf/` 与 `pow/`:WAF 及防 CC 挑战所需的专用 Lua 运行时脚本。
|
||||
* `waf_config.json` 与 `waf_ip_groups.json`:WAF 过滤引擎所需的结构化规则配置文件。
|
||||
* `pages_dir`:Pages 静态站点部署目录,默认位于 `data_dir/var/lib/openflare/pages`。当激活配置引用 Pages 部署时,Agent 会下载部署 zip、校验 checksum、解压到部署 release 目录,并切换 `deployments/{deployment_id}/current` 供 OpenResty `root`/`try_files` 读取。
|
||||
|
||||
### 2. 精细化的重载动作
|
||||
1. **备份当前配置**:在写入新文件之前,Agent 会将现有的配置文件复制到 `.backup` 临时目录下,保留完整的现场快照。
|
||||
2. **写入并替换占位符**:将最新拉取的模板写入,自动将模板中的绝对路径占位符(如 `__OPENFLARE_LUA_DIR__`、`__OPENFLARE_PAGES_DIR__`)替换为本地实际运行路径。
|
||||
3. **语法校验**:调用 `openresty -t -c <temp_nginx.conf>` 进行严格的语法测试。
|
||||
4. **平滑重载**:若校验通过,将新配置移至正式路径,执行 `openresty -s reload`。若 OpenResty 处于未启动状态,则使用当前配置拉起进程。
|
||||
5. **捕获异常**:校验或重载失败时,Agent 会截获标准错误输出(stderr),提取前 2000 个字符的详细报错信息。
|
||||
|
||||
---
|
||||
|
||||
## 发布与配置应用模型
|
||||
|
||||
OpenFlare 摒弃了动态 Patch 节点配置的落后方式,采用 **不可变配置版本发布模型**。
|
||||
|
||||
```text
|
||||
修改规则 -> 预览 / 查看 diff -> 发布 -> 生成完整配置版本 -> 激活版本 -> Agent 拉取 -> 本地应用 -> 上报结果
|
||||
```
|
||||
|
||||
### 1. 核心设计原则
|
||||
* **完整发布**:每次发布均是对当前控制面所有启用路由、证书、Pages 部署引用、全局与局部 WAF 规则进行一次性全量编译,生成带唯一 `checksum` 的完整版本。
|
||||
* **版本格式**:采用 `YYYYMMDD-NNN` 递增格式,确保版本历史直观、具备单调递增性。
|
||||
* **全局单激活版本**:系统同时只有一个处于 `active` 状态的全局配置版本。回滚时无需逆向打补丁,只需将历史某个健康版本的状态改为 `active`,Agent 重新拉取应用即可。
|
||||
|
||||
### 2. 三阶段容灾回滚机制
|
||||
当 Agent 发现配置应用(或平滑重载)失败时,将自动激活以下三阶段容灾防瘫痪链路:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[配置应用失败] --> B[第一阶段: 尝试本地备份恢复]
|
||||
B -- 备份文件存在 --> C[写入本地备份文件]
|
||||
C --> D[执行 openresty -t 校验]
|
||||
D -- 校验成功 --> E[reload 恢复旧版本运行]
|
||||
D -- 校验失败 --> F[进入第二阶段]
|
||||
B -- 无备份 --> F[第二阶段: 写入内置安全兜底配置]
|
||||
F --> G[写入兜底 nginx.conf: 仅监听 80 端口]
|
||||
G --> H[启用 stub_status 健康检查]
|
||||
G --> I[其他路由统一返回 503 且拦截异常配置]
|
||||
G --> J[尝试拉起 OpenResty 维持基础存活]
|
||||
J --> K[进入第三阶段]
|
||||
E --> L[上报 Apply Warning]
|
||||
K --> M[本地阻断该异常版本重复应用]
|
||||
M --> N[上报 Apply Error 并保留详细报错]
|
||||
```
|
||||
|
||||
1. **第一阶段:本地备份回退**
|
||||
* Agent 尝试从前一步保存的 `.backup` 目录恢复主配置、路由及证书。
|
||||
* 写入备份文件后,重新执行 `openresty -t` 校验。若成功,重载回退并向 Server 上报 `Warning`(警告:应用新版本失败,已自动退回历史健康版本)。
|
||||
2. **第二阶段:内置安全兜底运行**
|
||||
* 若本地不存在备份配置(如首次部署即配置错误),或者回退备份配置依然校验失败,Agent 将激活最终自愈机制——写入**内置安全兜底配置**。
|
||||
* **安全兜底配置规范**:
|
||||
* 仅监听 `80` 端口,不包含任何用户的真实反代路由。
|
||||
* 除 `/openflare/stub_status` 健康监测路由返回正常外,其他一切访问请求统一返回状态码 `503 Service Unavailable`,响应体固定为 `OpenFlare: No Valid Configuration`。
|
||||
* 尝试以此极简配置拉起 OpenResty。这能够确保 Nginx 进程自身不瘫痪,保留了底层的健康检查与探针通道,防止容器/Pod 因健康检查失败而被调度系统不断销毁重启,同时保护了敏感路由的安全性。
|
||||
3. **第三阶段:本地配置阻断**
|
||||
* Agent 会将当前导致崩溃的配置 `version + checksum` 记录在本地状态库的阻断名单中。
|
||||
* 在控制面未激活新的配置(`checksum` 发生变化)之前,Agent 心跳将阻断对此异常版本的重复同步拉取,防止节点陷入“心跳 -> 拉取崩溃配置 -> 崩溃回滚”的死循环。
|
||||
|
||||
### 3. WAF IP 组运行时异步同步
|
||||
为了避免高频变动的恶意 IP 黑名单频繁触发主配置的全量发布与 reload(平滑重载对 Nginx 依然有微小的 CPU 与连接开销),IP 组成员采用了与发布版解耦的**异步差分同步设计**:
|
||||
|
||||
* **静态发布快照**:发布生成的 `waf_config.json` 中仅包含规则组对 IP 组的引用关系(即 `ip_whitelist_group_ids` / `ip_blacklist_group_ids`),不包含具体的 IP 成员列表。
|
||||
* **心跳差分对比**:Agent 在心跳包中上报本地已缓存 IP 组的 MD5 Checksum 映射表。
|
||||
* **差分下发**:Server 比对当前激活版本引用的 IP 组哈希,仅向 Agent 下发缺失或发生变更的 IP 组成员,写入本地 `waf_ip_groups.json`,实现极速差分同步。
|
||||
* **WebSocket 实时通知**:当 Server 手动更新 IP 组、订阅源自动同步成功、或安全规则自动触发临时封禁时,Server 会立即通过 WebSocket 广播受影响的 IP 组更新包,Agent 接收落地并即时生效,全程**无须 reload Nginx**。
|
||||
|
||||
---
|
||||
|
||||
## 设计约束
|
||||
|
||||
为保证数据与控制链路的安全边界,Agent 代码编写与二次开发必须严格遵守以下工程约束:
|
||||
|
||||
1. **零特权指令通道**:Server 绝对禁止向 Agent 传递任何任意 shell 命令或远程执行脚本(如 exec/eval 等)。所有系统控制原语(如启动、停止、重载、更新)必须硬编码在 Agent 二进制内部。
|
||||
2. **严格的 Token 过滤与前缀验证**:Agent 侧向 Server 请求资源时,接口端点固定以 `/api/agent/` 为前缀,并强制携带 `X-Agent-Token` 进行签名或令牌核验。
|
||||
3. **节点自治原则**:Agent 须具备完备的离线工作能力。在与 Server 失去连接期间,本地 OpenResty 必须依靠本地已落地的配置保持反向代理服务的绝对正常运行。
|
||||
+154
-37
@@ -1,57 +1,174 @@
|
||||
# 系统架构
|
||||
|
||||
OpenFlare 由 Server、Agent 与节点本地 OpenResty 组成。
|
||||
你会学到:OpenFlare 的整体架构、各核心组件(Server, Agent, OpenResty, Relay, Client)的职责分工,以及主要数据与请求流的宏观流向。
|
||||
|
||||
OpenFlare 是一套自托管的 OpenResty 控制面。它在物理上由 Server(控制面)、Agent(配置落地端)、节点本地 OpenResty(数据面)、内网穿透组件(Relay 与 OpenFlared,数据面扩展)以及管理端前端组成。
|
||||
|
||||
---
|
||||
|
||||
## 流量路径概览
|
||||
|
||||
根据不同的网站上游类型,OpenFlare 支持三种不同的数据面流量路径:
|
||||
|
||||
### 1. 标准反代流量路径
|
||||
```text
|
||||
OpenFlare Server (Gin + SQLite/PostgreSQL + Web UI)
|
||||
|
|
||||
| HTTP API / Config Pull
|
||||
v
|
||||
OpenFlare Agent (register / heartbeat / sync / apply / update)
|
||||
|
|
||||
v
|
||||
Local OpenResty or Docker OpenResty
|
||||
|
|
||||
v
|
||||
Origin
|
||||
Browser
|
||||
|
|
||||
| HTTPS/HTTP request
|
||||
v
|
||||
OpenResty (WAF, TLS, Rate Limit)
|
||||
|
|
||||
| reverse proxy (proxy_pass)
|
||||
v
|
||||
Origin Server (直连公网/局域网上游)
|
||||
```
|
||||
|
||||
## Server
|
||||
### 2. 内网穿透流量路径
|
||||
适用于内网受限服务器上的源站服务接入:
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
| HTTPS/HTTP request
|
||||
v
|
||||
OpenResty (Agent 宿主机, TLS/WAF)
|
||||
|
|
||||
| proxy_pass http://localhost:vhost_port (Host header preserved)
|
||||
v
|
||||
OpenFlareRelay (frps) <-- 与 Agent 同机部署,提供中继
|
||||
|
|
||||
| frp tunnel protocol (Host header routing)
|
||||
v
|
||||
OpenFlared (frpc) <-- 内网受限服务器
|
||||
|
|
||||
| HTTP/HTTPS forward
|
||||
v
|
||||
Internal Service (192.168.x.x)
|
||||
```
|
||||
|
||||
`openflare_server` 是单体控制面:
|
||||
### 3. Pages 静态托管流量路径
|
||||
适用于预构建的单页应用(SPA)或静态网站托管:
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
| HTTPS/HTTP request
|
||||
v
|
||||
OpenResty (Agent, TLS/WAF)
|
||||
|
|
||||
+---> [静态服务] root/try_files ---> Agent 本地 Pages 部署目录
|
||||
|
|
||||
+---> [API 反代] proxy_pass ---> 后端 API 服务 (如果启用了 API 代理)
|
||||
```
|
||||
|
||||
* Gin
|
||||
* GORM
|
||||
* SQLite / PostgreSQL
|
||||
* 现有登录与 Session 体系
|
||||
* 认证源与外部账号绑定
|
||||
* 托管 `openflare_server/web` 静态构建产物
|
||||
---
|
||||
|
||||
Server 负责管理端 UI 与 API、Agent API、配置渲染、版本发布、数据存储与聚合查询。
|
||||
## 组件职责
|
||||
|
||||
认证源登录由 Server 统一处理。管理端配置 `github` 或 `oidc` 认证源后,登录页从 `/api/status` 获取已启用认证源列表;OAuth/OIDC callback 仍回到管理端前端页面,再由前端调用 Server API 完成 code 交换、账号绑定与 Session 建立。
|
||||
| 组件 | 职责 | 详细设计参考 |
|
||||
| --------------- | ---------------------------------------------------------------------- | ------------ |
|
||||
| **Server** | 管理端 UI/API、控制面状态持久化、配置编译渲染、发布版本控制、Pages 部署包存储、Uptime Kuma 监控同步与登录验证码防护 | [Agent 与发布模型](./agent-design.md) / [Uptime Kuma 监控同步设计](./kuma-design.md) / [登录验证码设计](./login-captcha.md) |
|
||||
| **Agent** | 周期心跳与 WS 同步、静态资源包拉取与解压、OpenResty 配置写入/校验/重载与自愈 | [Agent 与发布模型](./agent-design.md) |
|
||||
| **OpenResty** | 接收真实流量,执行 WAF 过滤、PoW 防护、Basic Auth 认证与静态/反代服务 | [WAF 设计](./waf-design.md) / [Pages 设计](./pages-design.md) |
|
||||
| **Relay** | 部署于边缘节点,管理 `frps` 守护进程生命周期,接受心跳派发的穿透中继配置 | [内网穿透设计](./tunnel-design.md) |
|
||||
| **OpenFlared** | 部署于内网,管理 `frpc` 进程组,向多个 Relay 建立反向隧道,上报连接状态 | [内网穿透设计](./tunnel-design.md) |
|
||||
| **Frontend** | Next.js 管理界面,提供路由、WAF、证书、节点、穿透隧道和 Pages 项目的可视化管理 | [开发约束](../guideline/Constraints.md) |
|
||||
|
||||
## Agent
|
||||
---
|
||||
|
||||
`openflare_agent` 是 Go 单体程序:
|
||||
## 组件架构与分工
|
||||
|
||||
* 单二进制
|
||||
* 节点本地执行
|
||||
* 优先使用 `openresty_path`
|
||||
* 未配置 `openresty_path` 时默认使用 Docker OpenResty
|
||||
### 1. Server (控制面)
|
||||
`openflare-server` 是 Go 编写的单体控制面:
|
||||
* 提供管理端 REST API,通过 `OPENFLARE_TOKEN` 请求头鉴权。
|
||||
* 包含配置编译器(Compiler),将数据库中的规则、证书与全局参数统一编译为不可变的配置快照及 OpenResty 物理配置文件文本。
|
||||
* 存储 Pages 部署 ZIP 包于本地 Artifacts 目录,并向 Agent 提供受控的下载接口。
|
||||
* 后台集成 Uptime Kuma 监控同步服务,自动为可用站点维护 HTTP 探测任务。
|
||||
* *详细设计请参阅:[Agent 与发布模型设计](./agent-design.md) 以及 [Uptime Kuma 监控同步设计](./kuma-design.md)*
|
||||
|
||||
Agent 负责首次注册、周期性心跳、配置同步、文件写入、`openresty -t`、reload、失败回滚、自更新与轻量采集。
|
||||
### 2. Agent (配置落地端)
|
||||
`openflare-agent` 是运行在节点本地的守护进程:
|
||||
* 启动后维持与控制面的周期性心跳,并通过可选的 WebSocket 接收实时的配置发布广播。
|
||||
* 负责拉取最新激活版本的配置文件及证书,写入本地目录,并通过 `openresty -t` 执行安全校验后平滑重载 (`reload`)。
|
||||
* 在本地处理 Pages 部署包的下载、SHA-256 校验与解压缩切换。
|
||||
* *详细设计请参阅:[Agent 与发布模型设计](./agent-design.md)*
|
||||
|
||||
## Frontend
|
||||
### 3. OpenResty (数据面)
|
||||
接收访客流量并执行最终的业务落地:
|
||||
* 流量入口,支持 HTTP/2、HTTP/3(QUIC)和 TLS 证书动态绑定。
|
||||
* 嵌入 Lua 逻辑,在 `access_by_lua` 阶段高效过滤 WAF 规则、验证工作量证明 (PoW) 挑战,并在此之后执行连接数/速率限制及基础缓存。
|
||||
* *详细设计请参阅:[WAF 设计文档](./waf-design.md) 与 [Pages 静态托管设计文档](./pages-design.md)*
|
||||
|
||||
`openflare_server/web` 是正式管理端前端:
|
||||
### 4. Relay 与 OpenFlared (穿透组件)
|
||||
扩展数据面反穿透能力:
|
||||
* `openflare-relay` 守护本地 `frps`,接受 Server 的配置派发,自动更新中继端口。
|
||||
* `openflared` 在内网守护一组 `frpc` 客户端进程,实现多中继就近建连与高可用容灾。
|
||||
* *详细设计请参阅:[内网穿透隧道设计文档](./tunnel-design.md)*
|
||||
|
||||
* Next.js App Router
|
||||
* React 19
|
||||
* TypeScript
|
||||
* Tailwind CSS
|
||||
* 静态导出后由 Go Server 托管
|
||||
---
|
||||
|
||||
## 数据与请求流概览
|
||||
|
||||
### 1. 配置发布与同步流
|
||||
```text
|
||||
管理端修改配置 -> 发布新版本 -> 生成全局唯一 Checksum 激活版本
|
||||
|
|
||||
+------------------+------------------+
|
||||
| (WebSocket 广播或周期 Heartbeat) |
|
||||
v v
|
||||
[边缘节点 Agent] [内网 OpenFlared]
|
||||
拉取最新 OpenResty 配置/证书 拉取最新 Tunnel 映射配置
|
||||
增量拉取/解压 Pages 静态部署包 生成/重写 frpc.toml
|
||||
Nginx 校验配置并平滑重载 (reload) 平滑重载或拉起 frpc 进程
|
||||
上报应用状态 (Success / Error) 上报隧道连接状态与活跃指标
|
||||
```
|
||||
* *同步与自愈的精细时序及回滚模型详见:[Agent 与发布模型设计](./agent-design.md)*
|
||||
|
||||
### 2. 静态托管与 API 代理流
|
||||
* 静态资源解压落地于 Agent 节点的 `deployments/{id}/current` 下,OpenResty 通过 `root`/`index`/`try_files` 指令在边缘直接向访客提供极低延迟的静态资源服务。
|
||||
* 当启用 API 代理时,OpenResty 自动根据站点配置的 `api_proxy_path`(如 `/api`)将 API 请求重写并转发(`proxy_pass`)给后端动态接口。
|
||||
* *部署包校验、解压逃逸防御及 Nginx 规则渲染详见:[Pages 静态托管设计文档](./pages-design.md)*
|
||||
|
||||
### 3. WAF 安全过滤流
|
||||
* WAF 引擎嵌入在 OpenResty 请求生命周期中。
|
||||
* 过滤规则直接从 Agent 落地在节点本地的 `waf_config.json` 及 `waf_ip_groups.json` 读取,判决逻辑白名单优先、黑名单层层过滤,完全在本地内存中完成,不产生数据库或网络 I/O 损耗。
|
||||
* *IP组增量同步、自动 IP 组计算与拦截响应机制详见:[WAF 设计文档](./waf-design.md)*
|
||||
|
||||
---
|
||||
|
||||
## 核心对象
|
||||
|
||||
当前有效实体包括 `proxy_routes`、`origins`、`config_versions`、`nodes`、`auth_sources`、`external_accounts`、`apply_logs`、`tls_certificates`、`managed_domains`、`node_request_reports`、`node_access_logs`、`node_metric_snapshots`、`traffic_analytics_rollups` 与 `node_health_events`。
|
||||
当前系统核心实体包括:
|
||||
|
||||
* **反代与配置**:`proxy_routes` (网站配置), `origins` (源站), `config_versions` (配置版本), `tls_certificates` (证书), `managed_domains` (托管域名).
|
||||
* **Pages 静态托管**:`pages_projects` (Pages项目), `pages_deployments` (不可变部署), `pages_deployment_files` (部署文件清单).
|
||||
* **节点与穿透**:`nodes` (节点), `tunnels` (隧道客户端), `node_system_profiles` (系统概况), `apply_logs` (应用日志).
|
||||
* **WAF 与安全**:`waf_rule_groups` (WAF规则组), `waf_ip_groups` (WAF IP组), `waf_rule_group_bindings` (网站WAF绑定).
|
||||
* **系统与账号**:`acme_accounts` (ACME账户), `dns_accounts` (DNS账户), `geoip_update_configs` (GeoIP更新配置).
|
||||
|
||||
---
|
||||
|
||||
## 关键设计决策
|
||||
|
||||
| 决策 | 原因 |
|
||||
| ------------------------------ | --------------------------------------------------------------------------- |
|
||||
| 完整配置版本,而不是在线 patch | 让预览、激活、历史和回滚有稳定边界,保证节点状态一致 |
|
||||
| Agent 主动拉取 | Server 不需要 SSH 权限,降低安全风险;支持 HTTP 与 WebSocket 双协议灵活切换 |
|
||||
| 全局单激活版本 | 降低控制面复杂度,保证所有节点默认一致;提供一键秒级回滚的稳定机制 |
|
||||
| 网站配置聚合多域名 | 支持单个业务站点共享站点级策略,同时支持按域名灵活绑定不同的 TLS 证书 |
|
||||
| 内网穿透基于 frp 整合 | 复用成熟隧道协议,避免自研隧道引起稳定性风险;其 Vhost 机制天然适配反代路由 |
|
||||
| 运行时配置与控制库解耦 | 如 WAF 运行时只读取本地 JSON 规则包,配置变更通过差分广播或快速重载热生效 |
|
||||
|
||||
---
|
||||
|
||||
## 贡献者阅读建议
|
||||
|
||||
修改系统架构或开发新功能前,请按以下顺序阅读:
|
||||
|
||||
1. **[产品边界](./index.md)**:了解 OpenFlare 核心定位与不允许逾越的设计边界。
|
||||
2. **[开发约束](../guideline/Constraints.md)**:掌握数据模型、API 约定、数据库迁移(Goose)与前端规范。
|
||||
3. **[Agent 与发布模型](./agent-design.md)**:理解版本快照同步及失败回滚的安全兜底逻辑。
|
||||
4. **细分领域设计**:
|
||||
* 穿透相关开发:阅读 [内网穿透隧道设计](./tunnel-design.md)。
|
||||
* WAF 相关开发:阅读 [WAF 设计](./waf-design.md)。
|
||||
* Pages 托管开发:阅读 [Pages 静态托管设计](./pages-design.md)。
|
||||
* 监控同步开发:阅读 [Uptime Kuma 监控同步设计](./kuma-design.md)。
|
||||
5. **[仓库结构](./index.md#仓库结构)**:明确各个物理目录分层职责,避免堆砌和重复开发。
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
# 开发约束
|
||||
|
||||
本文档融合原开发规范、前端规范与开发计划,是 OpenFlare `1.0.0` 之后的工程约束入口。
|
||||
|
||||
## 当前结论
|
||||
|
||||
* 第一版至第六版的主线能力已经全部完成。
|
||||
* `1.0.0` 是当前正式基线。
|
||||
* 已完成阶段的过程性任务以代码、测试与 Git 历史为准。
|
||||
* 新工作优先以缺陷修复、可维护性改进、文档与测试补强为主。
|
||||
|
||||
当前开发优先级:
|
||||
|
||||
1. 稳定性。
|
||||
2. 升级与回滚链路可靠性。
|
||||
3. 文档准确性。
|
||||
4. 测试覆盖补强。
|
||||
5. 在既有边界内的小步迭代。
|
||||
|
||||
## 变更准入
|
||||
|
||||
新需求进入实现前,按以下顺序判断:
|
||||
|
||||
1. 是否符合 [产品边界](./)。
|
||||
2. 是否符合本文档的后端、Agent 与前端约束。
|
||||
3. 是否会破坏现有发布、同步、回滚或升级主链路。
|
||||
4. 是否需要同步更新部署、配置、README 或文档站页面。
|
||||
|
||||
如果需求超出边界或引入新基础设施,应先更新设计文档,再开始实现。
|
||||
|
||||
任何合入正式基线的改动,至少应满足:
|
||||
|
||||
* 不破坏 Agent 心跳、同步、发布与回滚主链路。
|
||||
* 不破坏现有 OpenResty 主配置托管模型。
|
||||
* 不降低总览、节点详情与访问分析的既有可用性。
|
||||
* 有与风险相称的测试或联调验证。
|
||||
* 文档与代码保持一致。
|
||||
|
||||
## 技术基线
|
||||
|
||||
Server:
|
||||
|
||||
* Go 1.25+
|
||||
* Gin
|
||||
* GORM
|
||||
* SQLite / PostgreSQL
|
||||
* 现有登录体系
|
||||
|
||||
Agent:
|
||||
*
|
||||
* 单二进制
|
||||
* 节点本地执行
|
||||
* `openresty_path` 优先
|
||||
* 无 `openresty_path` 时默认 Docker OpenResty
|
||||
|
||||
Frontend:
|
||||
|
||||
* Next.js 15 App Router
|
||||
* React 19
|
||||
* TypeScript 5
|
||||
* Tailwind CSS 4
|
||||
* TanStack Query
|
||||
* React Hook Form + Zod
|
||||
* Zustand 仅用于轻量客户端状态
|
||||
* ESLint + Prettier
|
||||
* Vitest + Testing Library + Playwright
|
||||
* pnpm
|
||||
|
||||
## Server 分层
|
||||
|
||||
| 目录 | 职责 |
|
||||
| --- | --- |
|
||||
| `controller/` | 参数解析、调用 service、返回响应 |
|
||||
| `service/` | 业务逻辑、校验、事务编排、渲染 |
|
||||
| `model/` | 模型定义与持久化 |
|
||||
| `router/` | 路由注册 |
|
||||
| `middleware/` | 认证、鉴权、限流等横切逻辑 |
|
||||
| `common/` | 配置、全局状态与初始化入口 |
|
||||
| `utils/` | 纯工具函数与通用 helper |
|
||||
|
||||
禁止在 `controller/` 堆积业务逻辑,禁止在 `middleware/` 实现业务流程,禁止为简单需求新增平台层抽象。
|
||||
|
||||
## Agent 分层
|
||||
|
||||
Agent 保持现有模块边界:
|
||||
|
||||
* `config`
|
||||
* `heartbeat`
|
||||
* `sync`
|
||||
* `openresty` / `nginx`
|
||||
* `state`
|
||||
* `httpclient`
|
||||
* `protocol`
|
||||
* `internal/updater`
|
||||
|
||||
要求:
|
||||
|
||||
* 每个模块职责单一。
|
||||
* 外部命令调用集中封装。
|
||||
* 状态落盘与配置落盘分离。
|
||||
|
||||
## Frontend 分层
|
||||
|
||||
推荐目录:
|
||||
|
||||
```text
|
||||
app/
|
||||
components/
|
||||
features/
|
||||
lib/
|
||||
hooks/
|
||||
store/
|
||||
types/
|
||||
styles/
|
||||
tests/
|
||||
```
|
||||
|
||||
职责约束:
|
||||
|
||||
* `app/`:路由、布局、页面组装。
|
||||
* `features/`:按业务域组织模块。
|
||||
* `components/`:跨 feature 复用组件。
|
||||
* `lib/`:请求客户端、环境变量、工具函数、常量。
|
||||
* `store/`:少量跨页面 UI 状态。
|
||||
* `types/`:共享类型定义。
|
||||
|
||||
页面文件只负责获取路由参数、组织页面结构、调用 feature 组件;不应手写复杂 API 细节、复杂表单校验逻辑或维护大量彼此耦合的局部状态。
|
||||
|
||||
## 数据模型规范
|
||||
|
||||
当前有效实体:
|
||||
|
||||
* `proxy_routes`
|
||||
* `origins`
|
||||
* `config_versions`
|
||||
* `nodes`
|
||||
* `auth_sources`
|
||||
* `external_accounts`
|
||||
* `node_system_profiles`
|
||||
* `apply_logs`
|
||||
* `tls_certificates`
|
||||
* `managed_domains`
|
||||
* `node_request_reports`
|
||||
* `node_access_logs`
|
||||
* `node_metric_snapshots`
|
||||
* `traffic_analytics_rollups`
|
||||
* `node_health_events`
|
||||
* `options`
|
||||
|
||||
通用约束:
|
||||
|
||||
* 不新增平台化对象,除非设计文档明确要求。
|
||||
* `origins` 仅作为可复用源站地址目录,字段保持轻量。
|
||||
* `proxy_routes` 以“网站配置”作为聚合边界,必须包含唯一 `site_name` 与非空 `domains` 列表。
|
||||
* `proxy_routes.domains` 中的每个域名都必须全局唯一,列表第一项视为主域名。
|
||||
* `proxy_routes` 继续允许保存一个或多个上游地址用于负载均衡,但不引入独立 `origin_pool`。
|
||||
* 遗留 `domain` 字段只能作为 `domains[0]` 的兼容镜像;新代码不得继续以该字段作为唯一业务输入。
|
||||
* `proxy_routes` 如关联 `origins`,必须同时保存可直接渲染的 `origin_url`。
|
||||
* 上游统一使用 named `upstream` + keepalive;单上游如带 base path 或 query,应在 `proxy_pass` 上补回 URI,多上游仅允许纯 `scheme://host[:port]`。
|
||||
* 流量限制、反向代理与缓存配置当前都归属站点级 `proxy_routes`。
|
||||
* HTTPS 证书绑定必须通过与 `domains` 平行的 `domain_cert_ids` 逐域名保存;未绑定证书的域名不得参与 HTTPS 渲染。
|
||||
* `config_versions` 必须保存完整快照与渲染结果。
|
||||
* 全局同时只能有一个激活版本。
|
||||
* 回滚通过重新激活旧版本实现。
|
||||
* `nodes` 只保留控制面状态与低频摘要。
|
||||
* 观测数据必须按节点与时间窗口关联,快照与聚合结果采用追加式模型。
|
||||
* 原始访问明细必须有受控保留策略。
|
||||
* `auth_sources` 仅保存管理端第三方登录源配置,当前支持 `github` 与 `oidc`。
|
||||
* `external_accounts` 是第三方账号与本地用户的唯一绑定来源;旧 `users.github_id` 仅用于兼容迁移,不得作为新登录流程的业务输入。
|
||||
|
||||
## 数据库迁移
|
||||
|
||||
任何涉及表结构、索引、列类型、分表规则或内部持久化元数据的修改,都必须同步提升数据库版本号。
|
||||
|
||||
数据库版本号定义在 `openflare_server/model`,不得只依赖 `AutoMigrate` 隐式升级存量数据库。
|
||||
|
||||
每次提升数据库版本号时,必须补充从上一版本升级到新版本的显式迁移方法。迁移方法必须包含升级后的校验逻辑;只有校验通过,才能写入新的数据库版本记录。
|
||||
|
||||
新包启动后必须先检查数据库当前版本,再按顺序逐步升级到目标版本;禁止跳过中间升级步骤直接写目标版本。
|
||||
|
||||
空库初始化可以直接建立当前版本结构,但初始化完成后仍必须执行同版本校验,并落库当前数据库版本。
|
||||
|
||||
如果迁移失败或校验失败,启动流程必须中止,且不得提升数据库版本记录。涉及数据库版本变更的提交,必须补充对应的迁移测试或等效回归测试。
|
||||
|
||||
## API 与鉴权
|
||||
|
||||
管理端与 Agent API 统一使用 JSON。成功与失败都必须返回清晰 `message`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
约定:
|
||||
|
||||
* Agent API 固定放在 `/api/agent/*`。
|
||||
* 总览与节点详情优先使用专用聚合接口。
|
||||
* 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。
|
||||
* 管理端继续复用现有登录、角色与 Session。
|
||||
* 第三方登录统一通过认证源 API 进入,认证源管理接口必须要求 Root Session。
|
||||
* `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。
|
||||
* 第三方账号未绑定且注册关闭时,应提供绑定已有账号流程,不得自动创建用户。
|
||||
* Agent 正式请求统一使用节点专属 `agent_token`。
|
||||
* 首次接入可使用全局 `discovery_token`。
|
||||
* Agent 请求头统一使用 `X-Agent-Token`。
|
||||
|
||||
禁止暴露远程 shell 或任意命令执行入口,禁止在日志中打印完整 Token,禁止绕过占位符约束保存不可渲染的主配置模板。
|
||||
|
||||
## 发布与运行
|
||||
|
||||
发布逻辑必须保持:
|
||||
|
||||
* 发布时读取全部启用的 `proxy_routes`。
|
||||
* 同时读取 OpenResty 主配置参数、反代性能参数与缓存参数。
|
||||
* 生成完整 OpenResty 配置。
|
||||
* 计算 `checksum`。
|
||||
* 写入 `config_versions`。
|
||||
* 通过切换 `is_active` 激活版本。
|
||||
|
||||
版本约束:
|
||||
|
||||
* 版本号格式固定为 `YYYYMMDD-NNN`。
|
||||
* 不在线修改历史版本。
|
||||
* 不做按节点分组的差异化版本。
|
||||
* 预览与 diff 是只读能力,不产生发布记录。
|
||||
|
||||
Agent 必须满足:
|
||||
|
||||
* 启动后读取或生成本地 `node_id`。
|
||||
* 周期性心跳与同步。
|
||||
* 常规同步优先依据 heartbeat 返回的版本摘要判断。
|
||||
* 发现新版本时先备份旧文件。
|
||||
* 写入主配置、路由配置与必要证书文件。
|
||||
* 写入新配置后以运行态恢复为目标执行激活,Docker 模式优先重建容器并确认容器保持运行。
|
||||
* 新配置激活失败时必须先尝试用目标配置恢复运行,再回滚到旧配置并重新拉起 OpenResty。
|
||||
* 回滚后 OpenResty 恢复正常时上报警告;回滚后仍无法恢复运行时上报失败。
|
||||
* 某个目标 `version + checksum` 一旦应用失败并回退,Agent 必须在本地状态中阻断该目标的重复应用。
|
||||
|
||||
## 前端请求、状态与类型
|
||||
|
||||
所有 API 请求必须统一经过 `lib/api/`:
|
||||
|
||||
* 统一处理 `success/message/data` 响应结构。
|
||||
* 统一处理鉴权失效、网络异常和通用错误消息。
|
||||
* 统一维护资源接口与请求路径。
|
||||
|
||||
状态分层:
|
||||
|
||||
* 服务端状态:TanStack Query。
|
||||
* 页面临时状态:组件内部 `useState`。
|
||||
* 跨页面 UI 状态:Zustand。
|
||||
|
||||
要求开启 TypeScript 严格模式,禁止滥用 `any`,API 响应、表单输入、业务实体必须有明确类型。
|
||||
|
||||
## 表单、交互、样式与主题
|
||||
|
||||
表单统一使用 React Hook Form 与 Zod。
|
||||
|
||||
高风险操作必须二次确认、展示操作对象名称,并明确成功与失败反馈。
|
||||
|
||||
样式原则:
|
||||
|
||||
* 统一使用 Tailwind CSS 与现有 token 体系。
|
||||
* 优先复用已有基础组件与布局组件。
|
||||
* 保持视觉层级、留白与语义颜色一致。
|
||||
|
||||
主题要求:
|
||||
|
||||
* 同时支持 `light`、`dark`、`system`。
|
||||
* 用户选择必须持久化。
|
||||
* 首屏尽量避免主题闪烁。
|
||||
|
||||
## 测试与交付
|
||||
|
||||
* 关键业务逻辑必须有单元测试或等效回归测试。
|
||||
* Agent 主链路修改必须验证同步、应用与回滚。
|
||||
* 前端页面至少覆盖加载态、空态、错误态与成功反馈。
|
||||
* Go 版本调整时,同步检查 `go.mod`、Dockerfile 与 CI 工作流。
|
||||
|
||||
## 后续维护方式
|
||||
|
||||
后续规划不再按“大版本阶段文档”维护,而采用以下方式:
|
||||
|
||||
* 产品边界变动:更新 [产品边界](./)。
|
||||
* 工程约束变动:更新本文档。
|
||||
* 部署与配置变动:更新 [部署说明](../guide/deployment.md)、[配置项](../reference/configuration.md) 与 README。
|
||||
|
||||
如果未来出现明确的新阶段目标,再单独新增专项计划文档;不要把已完成的历史计划继续堆回本文档。
|
||||
|
||||
当前专项“网站级规则与配置界面改造”的模型边界已纳入 [产品边界](./),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。
|
||||
+137
-83
@@ -1,115 +1,169 @@
|
||||
# 产品边界
|
||||
|
||||
OpenFlare 是一套自托管的 OpenResty 控制面,面向单团队或单组织内部运维场景,解决反向代理配置、节点同步、证书托管与基础观测的统一管理问题。
|
||||
你会学到:OpenFlare 是什么、当前稳定能力,以及开发时应遵守的核心产品边界与仓库结构目录分工。
|
||||
|
||||
当前稳定能力包括:
|
||||
OpenFlare 是一套自托管的 OpenResty 控制面,面向单团队或单组织内部运维场景。
|
||||
|
||||
| 能力 | 说明 |
|
||||
| --- | --- |
|
||||
| 反代规则管理 | 以网站配置为聚合边界,支持多域名与源站配置 |
|
||||
| 网站级配置 | 一条规则对应一个网站,可绑定一个或多个域名,并共享站点级配置 |
|
||||
| 源站管理 | 维护轻量源站目录,并允许网站保存可渲染的源站快照 |
|
||||
| 配置版本 | 支持预览、发布、激活、不可变历史与回滚 |
|
||||
| Agent 同步 | 支持注册、心跳、同步、应用结果上报与自更新 |
|
||||
| OpenResty 托管 | 管理主配置模板、性能参数、缓存参数与 Lua 资源 |
|
||||
| HTTPS/TLS | 托管证书与域名资产,并按域名绑定证书 |
|
||||
| 基础观测 | 聚合节点请求、资源快照、健康事件和访问分析 |
|
||||
| 节点管理 | 节点状态、令牌体系、部署与更新链路 |
|
||||
| 管理端前端 | 基于 Next.js 的正式管理端 |
|
||||
| 认证源登录 | 支持以认证源形式配置 GitHub 与标准 OIDC 登录入口,并允许第三方账号绑定已有本地用户 |
|
||||
---
|
||||
|
||||
默认工作方式:
|
||||
## 项目定位
|
||||
|
||||
* 所有节点消费同一份全局激活版本。
|
||||
* Server 保存配置与状态,不直接 SSH 管理节点。
|
||||
* Agent 是节点侧唯一受控落地入口。
|
||||
OpenFlare 适合需要统一管理多台 OpenResty 代理节点的团队,具备以下定位:
|
||||
* **控制与落地分离**:Server 控制面不直接 SSH 到代理节点,而是通过 Agent 主动拉取版本并应用。
|
||||
* **不可变配置发布**:采用完整的配置版本进行预览、发布、激活和一键回滚。
|
||||
* **一体化网关托管**:在同一个控制面内集成网站反代、TLS 证书自动续期申请、WAF 防护拦截、内网穿透(Tunnel)以及 Pages 静态网站托管。
|
||||
|
||||
## 核心对象
|
||||
**非本产品定位**:多租户云平台、Kubernetes Ingress Controller、服务网格或通用日志平台。
|
||||
|
||||
当前有效实体:
|
||||
---
|
||||
|
||||
* `proxy_routes`
|
||||
* `origins`
|
||||
* `config_versions`
|
||||
* `nodes`
|
||||
* `auth_sources`
|
||||
* `external_accounts`
|
||||
* `node_system_profiles`
|
||||
* `apply_logs`
|
||||
* `tls_certificates`
|
||||
* `managed_domains`
|
||||
* `node_request_reports`
|
||||
* `node_access_logs`
|
||||
* `node_metric_snapshots`
|
||||
* `traffic_analytics_rollups`
|
||||
* `node_health_events`
|
||||
## 当前能力
|
||||
|
||||
## 网站配置约束
|
||||
| 能力 | 说明 | 详细设计/使用指南 |
|
||||
| --- | --- | --- |
|
||||
| **反代配置管理** | 以网站规则(Proxy Route)为聚合边界,支持多域名与多上游负载均衡 | [新建反代配置](../guide/proxy-config.md) |
|
||||
| **配置版本控制** | 支持全局单一激活版本的预览、发布、不可变快照历史与秒级一键回滚 | [Agent 与发布模型](./agent-design.md) |
|
||||
| **WAF 安全防护** | 全局与自定义规则组,支持手动/自动/订阅型 IP 组,GeoIP 准入与 PoW CC 防护 | [WAF 设计](./waf-design.md) / [WAF 使用指南](../guide/waf-usage.md) |
|
||||
| **内网穿透** | 通过中继节点(Relay)与内网客户端(OpenFlared),反向穿透暴露内网 Web 服务 | [内网穿透设计](./tunnel-design.md) / [穿透使用指南](../guide/tunnel-usage.md) |
|
||||
| **Pages 静态托管** | 直接上传前端 zip 包,由边缘节点拉取并由 OpenResty 本地服务,支持 API 反代与 SPA Fallback | [Pages 静态托管设计](./pages-design.md) |
|
||||
| **TLS 证书自动续期** | 绑定 managed_domains 并通过 ACME 协议向 Let's Encrypt 申请/续期证书 | [新建反代配置](../guide/proxy-config.md) |
|
||||
| **多节点监控与观测** | 收集节点资源快照、健康事件,聚合请求指标与访问日志明细 | [系统架构](./architecture.md) |
|
||||
|
||||
`proxy_routes` 从“单域名规则”升级为“网站配置”聚合对象。一条记录对应一个网站,可绑定一个或多个域名,并共享一组站点级配置。
|
||||
---
|
||||
|
||||
约束:
|
||||
## 核心产品边界与约束
|
||||
|
||||
* `proxy_routes.site_name` 是网站的业务唯一标识。
|
||||
* `proxy_routes.domains` 至少包含一个域名,且 `domains[0]` 作为主域名。
|
||||
* 任一域名全局只能属于一个 `proxy_routes`。
|
||||
* 迁移期可保留 `proxy_routes.domain` 作为 `domains[0]` 的镜像字段,但业务读写与后续扩展必须以 `site_name` + `domains` 为准。
|
||||
* 网站级流量限制、反向代理与缓存配置当前按站点共享,不在同一网站内做域名级差异化配置。
|
||||
* HTTPS 允许在同一站点内按域名绑定证书。
|
||||
在开发与贡献代码时,**必须严格遵守**以下业务边界与技术约束,禁止为了临时需求而绕过限制:
|
||||
|
||||
## 源站约束
|
||||
### 1. 网站配置与上游约束
|
||||
* **单站点域名共享策略**:一条路由规则对应一个网站,该站点下的多域名共享限流、缓存与反代上游等配置,不支持在同一规则内为不同域名做差异化服务配置。
|
||||
* **上游类型互斥**:上游必须是直连地址(`direct`)、内网穿透(`tunnel`)或 Pages 静态托管(`pages`)三者之一,不允许在同一规则中混用。
|
||||
* **直连类型限制**:直连上游可以是纯 `http://` 或 `https://` 的单个或多个地址(多地址仅支持纯 `scheme://host[:port]`),不支持非 HTTP 协议(如 TCP/UDP)上游。
|
||||
|
||||
`origins` 只保存源站地址、展示名与备注,不承载协议、端口、路径、权重或健康检查策略。
|
||||
### 2. WAF 安全边界
|
||||
* **白名单优先原则**:白名单拥有绝对匹配权。若未命中白名单规则,才依次触发全局和自定义黑名单过滤。
|
||||
* **GeoIP 弱依赖性**:地域准入解析完全依赖节点本地 MaxMind 库。当 GeoIP 异常或解析失败时,系统必须自动忽略地域规则,**绝对不能**破坏 IP 组过滤和反代主链路的可用性。
|
||||
* **运行时数据解耦**:OpenResty 拦截时仅读取 Agent 同步至本地的 JSON,不与 Server 数据库通信。IP 组成员同步与版本发布解耦,通过 Chestsum 差分拉取以实现零重载平滑生效。
|
||||
|
||||
`proxy_routes` 可选关联一个 `origins` 记录,用于复用源站地址;规则仍保存完整 `origin_url` 快照以参与渲染与版本快照。
|
||||
### 3. 内网穿透边界
|
||||
* **仅限 HTTP 流量**:穿透组件仅支持 HTTP/HTTPS 协议(底层依靠 frp 虚拟主机 Vhost 机制实现单端口域名路由复用),暂不支持单独的 TCP/UDP 端口分配。
|
||||
* **中继配置静态化**:中继节点(Relay)配置相对静态,通过心跳被动获取,不纳入控制面的配置版本化管理体系。
|
||||
* **Tunnel 与 Node 体系隔离**:Tunnel 客户端在内网发起出向建连,与控制面托管的边缘 Node(公网节点)是独立的实体,使用专属的 `tunnel_token` 进行鉴权。
|
||||
|
||||
上游约束:
|
||||
### 4. Pages 静态托管边界
|
||||
* **Direct Upload 托管模式**:仅支持直接上传预构建的 ZIP 静态资源包。不支持外部 Git 仓库自动构建、边缘 Serverless 函数、动态 SSR 服务或生成的二级预览域名。
|
||||
* **包体硬上限限制**:为了保障边缘节点安全,ZIP 压缩包体最大 25 MiB,解压文件树不超过 1,000 个且总体积不超过 100 MiB。禁止上传含有任何软链接或目录跨越(Zip-Slip)的安全高危压缩包。
|
||||
|
||||
* `proxy_routes` 至少包含一个上游地址。
|
||||
* 为兼容历史数据保留 `origin_url` 主上游字段,也允许在同一规则内补充多个上游做负载均衡。
|
||||
* 上游统一渲染为带 keepalive 的 named `upstream`。
|
||||
* 单上游可附带 base path 或 query 并在 `proxy_pass` 中追加。
|
||||
* 多上游限定为纯 `scheme://host[:port]`。
|
||||
* `proxy_routes.origin_host` 为可选字段,用于回源时覆盖 `Host` 请求头。
|
||||
* 所有上游地址都必须为合法 `http://` 或 `https://`。
|
||||
### 5. 系统与版本边界
|
||||
* **全局单一激活版本**:所有节点拉取并消费同一份全局激活配置。不进行按节点分组的差异化配置发布。
|
||||
* **单租户架构**:OpenFlare 仅供单团队在受信任的内部网络部署使用。采用单租户设计,不支持细粒度的多用户角色或多租户资源隔离。
|
||||
|
||||
## HTTPS 约束
|
||||
---
|
||||
|
||||
`proxy_routes.domain_cert_ids` 用于记录与 `domains` 平行的域名证书绑定;值为 `0` 表示该域名不启用 HTTPS,仅保留 HTTP。
|
||||
## 仓库结构
|
||||
|
||||
发布渲染时:
|
||||
在贡献代码时,请严格遵守以下物理分层与目录分工,保持代码结构清晰:
|
||||
|
||||
* 带证书的域名按证书分组输出独立 `443 ssl` `server` 块。
|
||||
* 未绑定证书的域名不得被自动带入 HTTPS。
|
||||
* 必须将 `proxy_routes.domains` 中的全部域名一并纳入同一站点配置,避免同站点在版本快照中被拆散。
|
||||
| 路径 | 职责 |
|
||||
| ---------------------- | ---------------------------------------------------- |
|
||||
| `openflare-server` | Gin + GORM + SQLite/PostgreSQL 单体控制面 |
|
||||
| `openflare-server/web` | Next.js 15 App Router 管理端前端,由 Go Server 托管 |
|
||||
| `openflare-agent` | Go 单体 Agent,运行在节点侧 |
|
||||
| `openflare-relay` | Tunnel 中继代理,运行在公网边缘管理 frps 进程 |
|
||||
| `openflared` | Tunnel 客户端,运行在内网服务器侧管理 frpc 进程 |
|
||||
| `scripts` | 安装、自更新等系统辅助脚本 |
|
||||
| `docs` | VitePress 文档站、设计基线、开发规范、部署与配置文档 |
|
||||
| `docs/en` | 英文版文档 |
|
||||
|
||||
## 认证源约束
|
||||
### 1. Server 分层 (`openflare-server/`)
|
||||
|
||||
`auth_sources` 是管理端第三方登录入口的配置对象,当前仅支持 `github` 与 `oidc` 两类。启用后的认证源会显示在登录页。
|
||||
| 目录 | 职责 |
|
||||
| ------------- | ------------------------------------------------ |
|
||||
| `controller/` | 参数解析、调用 service、返回响应 |
|
||||
| `service/` | 业务逻辑、校验、事务编排、配置渲染 |
|
||||
| `model/` | 纯净实体模型类定义、旧迁移框架兼容与上下文注入 |
|
||||
| `model/goose/` | goose 迁移提供者、桥接逻辑、注册入口与具体迁移文件 |
|
||||
| `router/` | 路由注册 |
|
||||
| `middleware/` | 认证、鉴权、限流、CORS、Turnstile 验证等横切逻辑 |
|
||||
| `common/` | 配置、全局状态与初始化入口 |
|
||||
| `utils/` | 纯工具函数与通用 helper |
|
||||
| `job/` | 定时任务(各业务定时逻辑在独立文件中定义,cron.go 仅用于初始化调度) |
|
||||
| `upload/` | 运行时本地临时文件上传目录(在 .gitignore 中忽略) |
|
||||
| `logs/` | 运行时本地日志输出目录(在 .gitignore 中忽略) |
|
||||
| `docs/` | API 文档(Swagger) |
|
||||
| `data/` | 静态数据(如 GeoIP 数据库) |
|
||||
|
||||
`external_accounts` 保存认证源外部账号与本地用户的绑定关系。第三方账号首次登录时:
|
||||
### 2. Agent 模块 (`openflare-agent/`)
|
||||
|
||||
* 已绑定本地用户则直接登录。
|
||||
* 当前已有本地登录 Session 时,绑定到当前用户。
|
||||
* 未绑定且允许注册时,自动创建普通用户并绑定。
|
||||
* 未绑定且关闭注册时,只允许用户输入已有本地账号密码完成绑定。
|
||||
| 目录/模块 | 职责 |
|
||||
| ----------------------------- | -------------------------------------------- |
|
||||
| `cmd/agent/` | Agent 命令行启动入口及主函数 |
|
||||
| `internal/config/` | 配置读取与默认值 |
|
||||
| `internal/heartbeat/` | 心跳与版本摘要判断 |
|
||||
| `internal/sync/` | 配置拉取与应用编排 |
|
||||
| `internal/nginx/` | OpenResty 文件写入、校验、reload、启动与回滚 |
|
||||
| `internal/state/` | 本地状态与观测补报缓冲 |
|
||||
| `internal/httpclient/` | Server 通信 |
|
||||
| `internal/wsclient/` | WebSocket 客户端通信 |
|
||||
| `internal/protocol/` | Agent API 协议类型 |
|
||||
| `internal/updater/` | Agent 自更新逻辑 |
|
||||
| `internal/logging/` | 日志处理 |
|
||||
| `internal/observability/` | 可观测性(指标、链路等) |
|
||||
| `internal/geoipdata/` | GeoIP 数据处理 |
|
||||
| `internal/geoipupdate/` | GeoIP 数据更新 |
|
||||
| `internal/agent/` | 核心 Agent 逻辑与生命周期 |
|
||||
|
||||
旧 `users.github_id` 仅作为升级迁移来源,新的第三方账号登录与绑定关系必须以 `external_accounts` 为准。
|
||||
### 3. Frontend 分层 (`openflare-server/web/`)
|
||||
|
||||
## 版本与观测约束
|
||||
| 目录 | 职责 |
|
||||
| ------------- | -------------------------------------------- |
|
||||
| `app/` | Next.js App Router 路由、布局、页面组装 |
|
||||
| `features/` | 按业务域组织的功能模块 |
|
||||
| `components/` | 跨 feature 复用的 UI 组件 |
|
||||
| `lib/` | 请求客户端、环境变量、工具函数、常量 |
|
||||
| `store/` | 少量跨页面 UI 状态管理 |
|
||||
| `types/` | 共享类型定义 |
|
||||
| `styles/` | 全局样式 |
|
||||
| `tests/` | 前端单元测试与集成测试(Vitest、Playwright) |
|
||||
| `scripts/` | 构建和部署相关脚本 |
|
||||
| `public/` | 静态资源 |
|
||||
|
||||
* `config_versions` 必须保存完整快照、渲染结果与 `checksum`。
|
||||
* 全局同时只能有一个激活版本。
|
||||
* 回滚通过重新激活旧版本实现。
|
||||
* `nodes` 只承载控制面状态与低频摘要,不承载高频观测事实。
|
||||
* 指标、趋势和访问分析优先使用服务端聚合结果,而不是前端临时统计。
|
||||
* 访问明细只保留受控时间窗口,不演变成通用日志平台。
|
||||
### 4. Relay 模块 (`openflare-relay/`)
|
||||
|
||||
| 模块 | 职责 |
|
||||
| ---------------- | ------------------------------------------------ |
|
||||
| `cmd/` | Relay 命令行启动入口及初始化主函数 |
|
||||
| `internal/config/`| 本地配置文件解析与默认参数初始化 |
|
||||
| `internal/frps/` | 管理 frps 进程生命周期、端口与 Token 并监控运行 |
|
||||
| `internal/heartbeat/`| 周期性 HTTP 心跳通信、上报状态并获取更新请求 |
|
||||
| `internal/httpclient/`| Server 的通用 API 客户端调用工具类 |
|
||||
| `internal/observability/`| 采集本地宿主机、frps 的基础运行指标并进行预聚合 |
|
||||
| `internal/relay/` | 协调中继的核心生命周期、初始化与清理 |
|
||||
| `internal/state/` | 本地运行时状态、错误记录与持久化缓存 |
|
||||
| `internal/updater/`| Relay 升级检查、下载安装与重启机制 |
|
||||
| `internal/wsclient/`| 与 Server 保持的长连接 WebSocket 双向通信管道 |
|
||||
|
||||
### 5. OpenFlared (Client) 模块 (`openflared/`)
|
||||
|
||||
| 模块 | 职责 |
|
||||
| ---------------- | ------------------------------------------------ |
|
||||
| `cmd/` | Client 命令行启动入口及初始化主函数 |
|
||||
| `internal/config/`| 本地客户端配置加载与解析 |
|
||||
| `internal/flared/`| 内网穿透客户端的核心调度与状态管理机制 |
|
||||
| `internal/frpc/` | 热重载/动态生成多 Relay 的 `frpc.toml` 并监控 frpc |
|
||||
| `internal/heartbeat/`| 与控制面进行的心跳通信,包含 Token 校验机制 |
|
||||
| `internal/httpclient/`| 客户端通用 API 通信客户端 |
|
||||
| `internal/sync/` | 增量拉取最新 Tunnel 路由绑定关系、生成快照并应用 |
|
||||
| `internal/updater/`| 客户端自更新、新版检查与更新落地逻辑 |
|
||||
| `internal/wsclient/`| 用于实时监听 Server 端隧道配置变更推送的 WS 信道 |
|
||||
|
||||
---
|
||||
|
||||
## 文档维护原则
|
||||
|
||||
* 产品范围或系统边界变化时更新本文档。
|
||||
* 开发约束、代码规范、接口约定变化时更新 [开发约束](./development.md)。
|
||||
* 部署方式变化时更新 [部署说明](../guide/deployment.md) 与 README。
|
||||
* 配置项变化时更新 [配置项参考](../reference/configuration.md)。
|
||||
* 已完成阶段不再以“版本计划”形式回填。
|
||||
* 新阶段开始前,先补设计,再进入实现。
|
||||
* 产品范围或系统边界变化:更新本文档([产品边界](./index.md))。
|
||||
* 系统结构、组件分工变化:更新 [系统架构](./architecture.md)。
|
||||
* 发布、同步、回滚与 Agent 模型变化:更新 [Agent 与发布模型](./agent-design.md)。
|
||||
* 开发约束、代码规范、接口约定变化:更新 [开发约束](../guideline/Constraints.md)。
|
||||
* 部署方式变化:更新 [部署说明](../deployment/deployment.md) 与 README。
|
||||
* 配置项变化:更新 [配置项参考](../reference/configuration.md)。
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# Uptime Kuma 监控同步设计
|
||||
|
||||
你会学到:OpenFlare 与 Uptime Kuma 监控服务集成的设计背景、基于 Socket.IO 协议的控制流设计、以标签隔离为核心的防污染模型,以及差分增量同步的状态机比对逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 需求分析
|
||||
|
||||
在多节点的网关架构中,监控系统的状态与反向代理路由的状态通常是相互脱节的:
|
||||
1. **录入开销大**:每当网关控制面新增或下线一个站点,管理员都必须在监控系统(如 Uptime Kuma)中重复配置对应的探测地址与告警策略。
|
||||
2. **数据不一致**:当代理路由域名发生变更或切换 HTTPS 时,容易遗漏修改监控参数,导致监控系统误报或漏报。
|
||||
3. **环境污染隐患**:如果简单的在监控中执行全量“删除-重建”同步,不仅会清空监控系统中的历史统计指标和 SLA 曲线,还会影响到用户在此监控实例上自行配置的、与网关无关的其他监控任务。
|
||||
|
||||
为了解决这些痛点,OpenFlare 引入了基于客户端/服务器模式的 **Uptime Kuma 自动监控同步机制**,实现网关站点路由定义与可用性监测系统的强一致、低开销以及零污染同步。
|
||||
|
||||
---
|
||||
|
||||
## 核心架构设计
|
||||
|
||||
Uptime Kuma 同步子系统完全运行在 **Server 控制面** 的后台调度器中。
|
||||
|
||||
```text
|
||||
[ OpenFlare 控制面 / 数据库 ] [ Uptime Kuma 实例 ]
|
||||
│ │
|
||||
1. 定时 Cron 触发 (Job) │
|
||||
│ │
|
||||
2. 读取代理路由与选项配置 │
|
||||
│ │
|
||||
3. 连接 Socket.IO 接口 <──── 4. Socket.IO 握手 & 登录 ───┤
|
||||
│ │
|
||||
├────── 5. 校验 / 创建 "OpenFlare" 标签 ────────►│
|
||||
├────── 6. 比对监测站点属性与 Kuma 监控清单 ──────►│
|
||||
│ │
|
||||
└────── 7. 执行差分指令 (add / edit / delete) ─►│
|
||||
```
|
||||
|
||||
同步子系统不经过数据面的 Agent 节点,而是由 Server 通过 Uptime Kuma 暴露的 Socket.IO 端点直接交互。这种设计可以降低边缘节点的网络开销,并将鉴权凭证(Kuma 用户名与密码)安全收拢在控制面中。
|
||||
|
||||
---
|
||||
|
||||
## 标签隔离与防污染设计
|
||||
|
||||
为了在一个共享的 Uptime Kuma 实例中安全运行,而不干扰用户手动创建的其他监控项,设计上采用了 **专属标签隔离机制**:
|
||||
|
||||
1. **`OpenFlare` 专属标签**:
|
||||
* 同步程序首次连接时,会调用 `getTags` 接口拉取实例中的所有标签。
|
||||
* 检查是否存在名为 `OpenFlare` 的标签(默认颜色为靛蓝色 `#4f46e5`)。如果不存在,则通过 `addTag` 接口在 Kuma 中自动创建它。
|
||||
2. **过滤范围收拢**:
|
||||
* 同步任务在拉取 Uptime Kuma 的监控列表(`monitorList`)后,仅会保留**打有 `OpenFlare` 标签**的监控项。
|
||||
* 所有的修改比对(`editMonitor`)和下线清理(`deleteMonitor`)**仅在此过滤子集内进行**。任何未绑定 `OpenFlare` 标签的监控项对同步程序均是“隐形”的,实现了完美的防污染隔离。
|
||||
|
||||
---
|
||||
|
||||
## 差分同步状态机逻辑
|
||||
|
||||
同步程序每次执行时,会对 OpenFlare 本地配置与 Uptime Kuma 数据进行差分计算,根据比对结果执行不同的 Socket.IO 事件:
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> 检查站点状态与监控范围
|
||||
|
||||
state "检查监控范围" as Scope {
|
||||
[*] --> 校验站点是否启用并且在 Scope 内
|
||||
校验站点是否启用并且在 Scope 内 --> 在Scope内 : 是
|
||||
校验站点是否启用并且在 Scope 内 --> 不在Scope内 : 否
|
||||
}
|
||||
|
||||
不在Scope内 --> 检查Kuma中是否存在同名且带标签的监控
|
||||
检查Kuma中是否存在同名且带标签的监控 --> 执行清理 : 存在
|
||||
检查Kuma中是否存在同名且带标签的监控 --> 忽略 : 不存在
|
||||
|
||||
在Scope内 --> 检查Kuma中是否存在同名监控
|
||||
|
||||
state "比对属性" as Compare {
|
||||
[*] --> 检查是否存在
|
||||
检查是否存在 --> 新建监控项 : 否
|
||||
检查是否存在 --> 比对元数据 : 是
|
||||
比对元数据 --> 属性一致 : 匹配
|
||||
比对元数据 --> 属性不一致 : 不匹配
|
||||
}
|
||||
|
||||
新建监控项 --> 发送add指令并绑定Tag
|
||||
属性不一致 --> 发送editMonitor指令
|
||||
属性一致 --> 忽略
|
||||
|
||||
执行清理 --> 发送deleteMonitor指令
|
||||
忽略 --> [*]
|
||||
```
|
||||
|
||||
### 1. 监测 URL 规范化
|
||||
站点路由在 OpenFlare 中可配置多个域名,同步程序自动提取其主域名(Primary Domain)并根据是否启用 HTTPS 组装为标准的 `http://` 或 `https://` 前缀。
|
||||
|
||||
### 2. 比对属性清单
|
||||
如果同名且带标签的监控已存在,同步程序会细致比对以下 5 个关键字段是否与当前网关全局 Option 一致。只要有一个字段不匹配,便会触发更新:
|
||||
* **URL 地址**:`Url`
|
||||
* **探测频率**:`Interval`(默认 60s)
|
||||
* **重试次数**:`MaxRetries`
|
||||
* **重试间隔**:`RetryInterval`(默认 60s)
|
||||
* **请求超时**:`Timeout`(默认 48s)
|
||||
|
||||
---
|
||||
|
||||
## 调度器与高并发保护
|
||||
|
||||
1. **基于 Cron 的单线程执行**:
|
||||
* Server 周期性(每 1 分钟)通过后台的 Cron Job 探测是否达到配置的同步间隔(`UptimeKumaSyncInterval`)。
|
||||
* 任务内部设计了互斥锁(Mutex Locking)。如果前一次同步请求因为网络延迟等原因尚未结束,下一次调度将自动跳过,防止并发多个 Socket.IO 连接对 Uptime Kuma 实例造成 DDOS 冲击。
|
||||
2. **WebSocket 状态监听**:
|
||||
* 同步程序利用 Socket.IO 的事件监听机制,在连接建立后,必须等到监听到 `monitorList` 事件的完整列表推送后,才允许向下执行差分算法,以规避因为数据加载不完整导致误删监控项的边界情况。
|
||||
@@ -0,0 +1,124 @@
|
||||
# 登录验证码设计 (Login CAPTCHA Integration)
|
||||
|
||||
本文档阐述在 OpenFlare 控制面中引入基于 Proof-of-Work (PoW) 与无感浏览器指纹特征的开源 CAPTCHA 方案 —— Cap,以防止对登录 API 进行暴力破解与爬虫撞库攻击的设计。
|
||||
|
||||
---
|
||||
|
||||
## 1. 业务背景与产品范围
|
||||
|
||||
### 背景与痛点
|
||||
根据我们的系统安全分析,OpenFlare 的登录端点 `/api/user/login` 虽然配置了基于 IP 的限流限制,但由于缺少用户维度的防护机制,攻击者可使用代理池绕过 IP 限制对高权限账户(如 `root`)实施撞库和暴力破解。同时,对于系统登录页面,标准的视觉验证码对用户体验和无障碍不够友好。
|
||||
|
||||
### 产品范围与技术选型
|
||||
* **技术选型**:Cap (Proof-of-Work 驱动的无感无图像验证码解决方案)。
|
||||
- **核心原理**:客户端(Widget/网页)从服务器获取工作量证明 (PoW) 的难题,使用浏览器后台计算求解并将答案回传。服务器验证答案的正确性,完成人机识别。
|
||||
- **优势**:无感、无图像验证、不依赖任何外部第三方 API 节点(私密)、包极小。
|
||||
* **接入范围**:控制面 Server 登录 API(`/api/user/login`)以及前端登录页面。
|
||||
* **配置粒度**:支持管理员通过控制台 Option 表随时开启/关闭验证码(`CapLoginEnabled`)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统架构与交互时序
|
||||
|
||||
### 2.1 模块分工
|
||||
1. **Frontend (前端)**:
|
||||
* 在登录页面引入 `cap-widget`(React 19 自定义元素)。
|
||||
* 提交表单时,伴随提交由 Widget 求解出并得到的 `cap-token`。
|
||||
2. **Server (控制面后端)**:
|
||||
* 暴露 `POST /api/cap/challenge` 接口,为客户端分发 PoW 难题和签名的 JWT Token。
|
||||
* 暴露 `POST /api/cap/redeem` 接口,校验客户端提交的 PoW 解答并核发带有失效时间的登录凭证(Redeem Token)。
|
||||
* 将 Redeem Token 与对应过期时间保存在内存缓存/Redis 缓存中。
|
||||
* 在 `POST /api/user/login` 接口中,若启用了验证码保护,先校验并消耗(单次失效)对应的 `cap-token`。
|
||||
|
||||
### 2.2 验证流时序图
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor User as 用户
|
||||
participant Browser as 浏览器 (前端 Web)
|
||||
participant Server as OpenFlare Server (后端)
|
||||
participant Cache as 内存/Redis 缓存
|
||||
|
||||
User->>Browser: 打开登录页面
|
||||
Browser->>Server: POST /api/cap/challenge (获取难题)
|
||||
Server->>Browser: 返回 {challenge, token, expires} (JWT 格式)
|
||||
Note over Browser: Widget 在后台(WASM/Worker)执行 PoW 难题计算
|
||||
Browser->>Server: POST /api/cap/redeem (提交 solutions + token)
|
||||
alt 校验 PoW 解答通过
|
||||
Server->>Cache: 存储 Redeem Token (tokenKey:expires)
|
||||
Server->>Browser: 返回 {success: true, token} (即 cap-token)
|
||||
else 校验失败
|
||||
Server->>Browser: 返回 {success: false, reason}
|
||||
end
|
||||
User->>Browser: 输入账号密码,点击登录
|
||||
Browser->>Server: POST /api/user/login (在 HTTP 请求头中携带 X-Cap-Token)
|
||||
alt CapLoginEnabled = true
|
||||
Server->>Server: Middleware (CapAuth) 校验并消费 X-Cap-Token
|
||||
alt token 合法且未过期且未被消费
|
||||
Server->>Server: c.Next() -> 执行常规登录逻辑 (密码 Bcrypt 校验)
|
||||
Server->>Browser: 返回登录成功 (JWT session)
|
||||
else token 无效或已被消费
|
||||
Server->>Browser: 拦截并返回验证码错误 (401 Unauthorized)
|
||||
end
|
||||
else CapLoginEnabled = false
|
||||
Server->>Server: c.Next() -> 执行常规登录逻辑
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心接口与数据模型
|
||||
|
||||
### 3.1 接口定义
|
||||
|
||||
#### 1. 获取难题 (GET/POST /api/cap/challenge)
|
||||
* **请求方式**:`POST`
|
||||
* **接口权限**:公开
|
||||
* **响应负载**:
|
||||
```json
|
||||
{
|
||||
"challenge": {
|
||||
"c": 50,
|
||||
"s": 32,
|
||||
"d": 4
|
||||
},
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjIjo1MCwicyI6MzIsImQiOjQsImV4cCI6MTcxNzY2MDgwMCwiaWF0IjoxNzE3NjYwMjAwLCJuIjoiMGExYjJjM2Q0ZTVmNiJ9.signature",
|
||||
"expires": 1717660800000
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 核销难题 (POST /api/cap/redeem)
|
||||
* **请求方式**:`POST`
|
||||
* **请求负载**:
|
||||
```json
|
||||
{
|
||||
"token": "challenge_jwt_token_here",
|
||||
"solutions": [12345, 67890, 54321]
|
||||
}
|
||||
```
|
||||
* **响应负载 (成功)**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"token": "random_id:ver_token",
|
||||
"expires": 1717661000000
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 登录接口 (POST /api/user/login)
|
||||
* **请求负载保持不变**:
|
||||
```json
|
||||
{
|
||||
"username": "root",
|
||||
"password": "your_password"
|
||||
}
|
||||
```
|
||||
* **验证码载体**:放置于 HTTP Request Header `X-Cap-Token` 中。
|
||||
|
||||
---
|
||||
|
||||
## 4. 重放攻击防护与安全性权衡
|
||||
1. **JWT 临时状态绑定**:难题在生成时就被签入 JWT payload,包含过期时间限制(10 分钟)。
|
||||
2. **Replay 拦截(Nonce 消耗)**:当客户端调用 `/redeem` 提交解答时,后端在缓存中标记该 JWT Signature 已使用。重复提交相同的解密包将返回 `already_redeemed`。
|
||||
3. **Redeem 一次性核销(单次失效)**:当客户端登录并提交 `cap-token` 时,后端在检验到合法性后立即从缓存中删除该 Key,防止黑客提取历史正确的 `cap-token` 进行重放登录。
|
||||
4. **验证机制无感化**:通过调整 `c (难题数)=50`,`d (难度)=4`,普通用户在桌面端和移动端只需 0.5 秒至 1.5 秒即可静默解出,极大地兼顾了用户体验和反爬效果。
|
||||
@@ -0,0 +1,218 @@
|
||||
# Pages 静态托管设计文档
|
||||
|
||||
你会学到:OpenFlare Pages 静态站点托管的架构设计、不可变部署与安全解压流程、OpenResty 的静态服务与 API 反向代理配置渲染,以及控制面与 Agent 的协同工作流。
|
||||
|
||||
---
|
||||
|
||||
## 需求分析
|
||||
|
||||
在现代 Web 运维中,除了动态应用的反向代理,静态前端站点(如 React、Vue 等构建的单页应用 SPA,或者 Hugo、VitePress 等静态生成器产物)的部署与托管也是极高频的场景。
|
||||
传统方案中,静态站点的发布通常面临以下痛点:
|
||||
1. **发布与反代配置脱节**:前端构建产物上传到 Nginx 宿主机后,还需要手动或通过其他脚本修改 Nginx 虚拟主机配置,容易出错且缺乏版本控制。
|
||||
2. **多节点分发困难**:当控制面管理多台边缘节点时,将静态文件同步分发到所有节点,并确保文件一致性,需要维护复杂的同步脚本(如 rsync 等)。
|
||||
3. **回滚缺乏一致性**:一旦新前端包发布失败或存在严重缺陷,不仅要恢复静态文件,还要恢复对应的反代规则,很难做到原子回滚。
|
||||
|
||||
为了解决这些问题,OpenFlare 引入了受 Cloudflare Pages 启发的 **Pages 静态托管** 功能。该功能将“前端部署包上传”与“网站代理规则配置”合二为一,依托 OpenFlare 的 pull-based(拉取式)协同架构,实现静态文件分发与反代配置发布的强一致性、不可变性与一键秒级回滚。
|
||||
|
||||
---
|
||||
|
||||
## 核心功能
|
||||
|
||||
Pages 静态托管子系统包含以下核心能力:
|
||||
* **Direct Upload 部署模式**:支持直接上传预构建的 `.zip` 静态资源包,省去复杂的 Git 集成和构建环境依赖。
|
||||
* **不可变部署快照**:每次上传产生一个带唯一 ID 和 SHA-256 Checksum 的不可变部署记录。历史包永久保留,支持随时激活和回滚。
|
||||
* **SPA Fallback 支持**:支持对单页应用(SPA)进行 Fallback 路由配置,请求找不到静态文件时自动重定向到入口文件。
|
||||
* **内置 API 反代服务**:支持在 Pages 规则内一键启用 API 代理,消除跨域问题,将请求转发给指定的后端服务。
|
||||
* **安全包校验与解压缩**:内置 Zip-Slip 路径逃逸防御、防软链接劫持、文件大小/数量硬上限控制,保障节点物理安全。
|
||||
|
||||
---
|
||||
|
||||
## Pages 静态托管架构
|
||||
|
||||
Pages 静态托管在逻辑上分为 **控制面 (Control Plane)** 与 **数据面 (Data Plane)**。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
%% 数据流
|
||||
Browser[1. 浏览器 / 访客] -->|HTTPS 请求 / 流量| OpenResty[2. OpenResty / WAF]
|
||||
OpenResty -->|1. 静态服务 try_files| StaticFiles[3. 边缘节点本地静态目录 current]
|
||||
OpenResty -->|2. 转发 API 代理| BackEnd[4. 后端 API 服务]
|
||||
|
||||
%% 控制流与心跳
|
||||
Server[OpenFlare Server 控制面] <-->|Agent API / Heartbeat| Agent[openflare-agent 进程]
|
||||
Server -.->|5. 存储 ZIP 部署包| LocalStore[(Server 本地存储)]
|
||||
|
||||
Agent -->|1. 发现新版本| Server
|
||||
Agent -->|2. 下载部署包| Server
|
||||
Agent -->|3. 校验并解压缩| StaticFiles
|
||||
Agent -->|4. 应用并 Reload| OpenResty
|
||||
|
||||
style Browser fill:#f9f,stroke:#333,stroke-width:2px
|
||||
style StaticFiles fill:#9f9,stroke:#333,stroke-width:2px
|
||||
style Server fill:#f96,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
* **控制面(Control Plane)**:Server 接收前端上传的部署包,并将包存储于本地磁盘,元数据写入数据库。配置发布时,编译出带有 `pages_deployment` 详情的不可变全局版本快照。
|
||||
* **数据面(Data Plane)**:Agent 在心跳同步中发现版本更新并引用了 Pages 部署,通过专属 API 下载对应的部署包并执行校验解压缩。OpenResty 拦截域名请求,在本地提供静态文件服务。
|
||||
|
||||
---
|
||||
|
||||
## 数据模型与元数据设计
|
||||
|
||||
### 1. 核心数据库实体
|
||||
* **Pages 项目 (`pages_projects`)**:
|
||||
* 记录项目的业务名称、Slug 标识(URL 友好型)、启用状态、静态服务根目录(RootDir,可为空)、入口文件名(EntryFile,默认 `index.html`)、SPA Fallback 设置,以及 API 反向代理配置(APIProxyPath, APIProxyPass, APIProxyRewrite)。
|
||||
* **Pages 部署 (`pages_deployments`)**:
|
||||
* 记录单次上传生成的不可变快照。包含:部署号 (DeploymentNumber, 递增序列)、SHA-256 Checksum 校验和、部署状态 (uploaded/active)、部署包的本地存储路径、解压后的文件数与总字节数。
|
||||
* **部署文件清单 (`pages_deployment_files`)**:
|
||||
* 存储每次部署的完整静态文件树路径、文件大小及单个文件哈希。用于审计和后续校验。
|
||||
|
||||
### 2. 路由关联与快照
|
||||
`proxy_routes` 路由规则通过 `upstream_type = "pages"` 及 `pages_project_id` 关联 Pages 项目。当路由类型为 `pages` 且该项目存在已激活的部署时,才允许将该路由加入发布流程。
|
||||
发布时生成的版本快照中包含 `snapshotPagesDeployment`,主要结构为:
|
||||
```json
|
||||
{
|
||||
"project_id": 1,
|
||||
"project_slug": "my-spa-app",
|
||||
"deployment_id": 12,
|
||||
"deployment_number": 3,
|
||||
"checksum": "a7b3c2...",
|
||||
"entry_file": "index.html",
|
||||
"spa_fallback_enabled": true,
|
||||
"spa_fallback_path": "/index.html",
|
||||
"api_proxy_enabled": true,
|
||||
"api_proxy_path": "/api",
|
||||
"api_proxy_pass": "http://api.internal:8000",
|
||||
"api_proxy_rewrite": "/api/(.*) /$1",
|
||||
"local_root": "__OPENFLARE_PAGES_DIR__/deployments/12/current"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server 端 (控制面) 职责与生命周期
|
||||
|
||||
### 1. ZIP 包安全校验与分析
|
||||
为了避免不可信的用户上传恶意压缩包攻击服务器,控制面在 `UploadPagesDeployment` 时执行严格的流式校验:
|
||||
* **大小限制**:ZIP 压缩包不得超过 25 MiB(保守的 V1 默认值),且展开后的解压总体积不得超过 100 MiB。
|
||||
* **数量限制**:压缩包中包含的静态文件总数不得超过 1,000 个。
|
||||
* **软链接阻断**:遍历 ZIP 文件,一旦检测到任何软链接 (`os.ModeSymlink`),立即抛出错误并拒绝上传,防御软链接劫持攻击。
|
||||
* **Zip-Slip 防御**:对每个压缩文件路径进行 `Clean` 并检查是否包含 `..` 或以 `/` 开头,防御目录跨越漏洞,防止写入系统敏感路径。
|
||||
* **入口文件校验**:项目指定的入口文件(例如 `index.html`,可在 `project.RootDir` 下)必须在 ZIP 压缩包中存在,否则拒绝上传。
|
||||
* **公共根目录去噪**:许多打包工具(如 GitHub 导出的 zip)会包含一个多余的主文件夹作为公共根前缀。控制面自动探测公共根前缀并将其安全剥离。
|
||||
|
||||
### 2. 部署包存储规划
|
||||
控制面仅将 zip 文件存储在本地存储目录 `artifacts/{project_slug}/{checksum}.zip`,并在数据库中记录路径和清单。**大体积静态包不写入 config_versions 记录和任何配置推送通道**,以保障控制面数据同步的轻量与高效。
|
||||
|
||||
---
|
||||
|
||||
## Agent 端 (数据落地) 职责与自愈
|
||||
|
||||
Agent 运行在各边缘代理节点上,在应用配置版本前,必须先将 Pages 静态资源“原子”地拉取到节点本地。
|
||||
|
||||
### 1. 校验式增量拉取
|
||||
1. Agent 解析激活配置中的 `SourceConfigJSON`,检索出所有 `UpstreamType == "pages"` 的路由引用的部署 `DeploymentID` 和 `Checksum`。
|
||||
2. 检查本地部署目录是否存在正确的版本标记文件 `.openflare-pages.json`,且 `Checksum` 匹配。
|
||||
3. 若不匹配,通过专属接口 `GET /api/agent/pages/deployments/:id/package` 下载对应的部署包。下载请求头必须携带节点独有的 `X-Agent-Token` 用于 Server 鉴权。
|
||||
|
||||
### 2. 安全解压缩与原子切换
|
||||
为了保证配置应用过程的“无缝”且能在出错时立即回滚:
|
||||
1. Agent 将下载的部署包数据写入临时目录,并重新计算 SHA-256 Checksum。如果与配置指明的 checksum 不符,立即报错并阻断发布流程。
|
||||
2. 解压部署包至临时目录 `releases/{checksum}.tmp`。解压时同样执行 Zip-Slip 目录跨越和软链接校验防御。
|
||||
3. 解压成功后,写入标记文件 `.openflare-pages.json`。
|
||||
4. 清理 `releases/{checksum}` 目录,将整个临时目录重命名为 `releases/{checksum}`。
|
||||
5. **原子切换**:建立拷贝当前部署的物理副本到目标位置 `deployments/{deployment_id}/current`。切换前先备份上一版本的 `current`,一旦重载配置失败,Agent 能够快速恢复 `current` 目录并回滚 OpenResty。
|
||||
6. **定时清理**:每次配置成功应用后,Agent 自动比对本地部署目录,将所有不活跃的(即未被当前激活版本引用的)历史部署包和文件夹进行物理删除,释放磁盘空间。
|
||||
|
||||
---
|
||||
|
||||
## OpenResty (静态服务与代理) 配置渲染
|
||||
|
||||
对于 Pages 托管站点,控制面自动渲染对应的 `server` 块,取代常规代理路由中的 `proxy_pass`。
|
||||
|
||||
### 1. 静态服务指令渲染
|
||||
* **`root` 与 `index`**:
|
||||
Server 根据配置将 `root` 指向 Agent 的 Pages 动态目录占位符 `__OPENFLARE_PAGES_DIR__/deployments/{deployment_id}/current`,并在此基础上追加项目的 `RootDir`。`index` 指向设置的入口文件。
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name myapp.example.com;
|
||||
|
||||
root "/var/lib/openflare/pages/deployments/12/current";
|
||||
index "index.html";
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. try_files 与 SPA Fallback 机制
|
||||
* **禁用 SPA Fallback (默认)**:
|
||||
仅匹配物理存在的文件,否则返回 strict 404:
|
||||
```nginx
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
```
|
||||
* **启用 SPA Fallback**:
|
||||
若请求的文件不存在,重定向到项目配置的入口 Fallback 文件(通常为 `/index.html`):
|
||||
```nginx
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. API 反向代理与重写 (Rewrite) 渲染
|
||||
当静态前端项目需要请求后端 API 且不希望面临跨域问题时,可开启 API 反代。OpenResty 渲染器会自动在其对应的静态 `server` 块内嵌套专属的 API `location` 分支:
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name myapp.example.com;
|
||||
...
|
||||
# API 代理路径匹配
|
||||
location /api {
|
||||
# 如果配置了 Rewrite 规则,应用重写逻辑
|
||||
rewrite ^/api/(.*)$ /v1/$1 break;
|
||||
rewrite ^/api$ / break;
|
||||
|
||||
proxy_pass http://api.internal:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 交互逻辑与同步流程
|
||||
|
||||
一次完整的 Pages 上传与全局生效的生命周期如下:
|
||||
|
||||
```text
|
||||
[ 前端管理员 ] [ Server (控制面) ] [ Agent (数据落地) ] [ OpenResty ]
|
||||
| | | |
|
||||
|--- 1. 上传 ZIP 包 ----->| | |
|
||||
| |--- 2. 安全校验与解压分析 ----| |
|
||||
| |--- 3. 归档包与持久化清单 ---| |
|
||||
| | | |
|
||||
|--- 4. 绑定路由并发布 -->| | |
|
||||
| |--- 5. 生成新配置版本并广播 ->| |
|
||||
| | | |
|
||||
| | |--- 6. 下载 ZIP 部署包 -->|
|
||||
| | |<-- 7. 返回文件数据 -------|
|
||||
| | | |
|
||||
| | |--- 8. 强一致性 Checksum -|
|
||||
| | |--- 9. 安全解压缩 -------|
|
||||
| | |--- 10. 原子切换 current -|
|
||||
| | |--- 11. 测试与重载配置 ---->|
|
||||
| | |<-- 12. 重载成功 ---------|
|
||||
| |<-- 13. 上报 Apply Success | |
|
||||
| | | |
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
# 发布模型
|
||||
|
||||
OpenFlare 的发布模型以完整配置版本为中心,而不是在线修改节点配置。
|
||||
|
||||
标准链路:
|
||||
|
||||
```text
|
||||
修改规则 -> 预览/查看 diff -> 发布 -> 生成完整配置版本 -> 激活版本 -> Agent 拉取 -> 本地应用 -> 上报结果
|
||||
```
|
||||
|
||||
## 发布规则
|
||||
|
||||
Server 发布时必须:
|
||||
|
||||
1. 读取全部启用的 `proxy_routes`。
|
||||
2. 读取 Server 侧 OpenResty 主配置与结构化参数。
|
||||
3. 渲染完整 OpenResty 配置。
|
||||
4. 计算 `checksum`。
|
||||
5. 写入 `config_versions`。
|
||||
6. 切换激活版本。
|
||||
7. 让 Agent 在后续 heartbeat 中发现并应用。
|
||||
|
||||
版本号格式固定为 `YYYYMMDD-NNN`。
|
||||
|
||||
## 不可变历史
|
||||
|
||||
历史版本不可变。回滚不是修改旧版本,而是重新激活旧版本。
|
||||
|
||||
全局同时只能有一个激活版本,当前不做按节点分组的差异化版本。
|
||||
|
||||
## Agent 应用策略
|
||||
|
||||
Agent 发现新版本后会备份旧文件,写入主配置、路由配置、证书与必要 Lua 资源,再执行配置校验和 reload。
|
||||
|
||||
如果新配置激活失败,Agent 必须尝试恢复运行;回滚成功时上报警告,回滚后仍无法恢复运行时上报失败。
|
||||
|
||||
某个目标 `version + checksum` 一旦应用失败并回退,Agent 会在本地状态中阻断该目标重复应用。只有远端激活版本或 checksum 发生变化,才允许再次尝试。
|
||||
@@ -0,0 +1,130 @@
|
||||
# 内网穿透隧道设计文档
|
||||
|
||||
你会学到:OpenFlare 内网穿透隧道的架构设计、双端管控组件(Relay 与 Client)的内部原理、交互逻辑以及数据面与控制面的通信流程。
|
||||
|
||||
---
|
||||
|
||||
## 需求分析
|
||||
|
||||
在典型的 Web 应用托管场景中,许多源站(Origin Server)部署在内网环境(如本地开发机、局域网服务器或受防火墙限制的内网集群)。这些服务器通常:
|
||||
1. **无公网 IP**:无法直接被公网流量访问。
|
||||
2. **安全合规限制**:不允许随意在边界路由器上配置端口映射(NAT)。
|
||||
3. **动态 IP 变动**:传统的 DDNS 方案延迟高且极不稳定。
|
||||
|
||||
为了让内网源站能够无缝接入 OpenFlare 全局数据网关并享受 WAF 地域防护、TLS 证书托管等增值服务,OpenFlare 设计了基于 **反向中继穿透隧道** 的整体解决方案。在该架构中,公网边缘节点作为反代入口和流量中继,内网侧仅需发起安全出向连接,即可实现公网流量安全、稳定地反向穿透到内网源站。
|
||||
|
||||
---
|
||||
|
||||
## 核心功能
|
||||
|
||||
内网穿透隧道子系统包含以下核心能力:
|
||||
|
||||
* **Relay 节点动态管理**:由控制面动态派发中继服务(frps),动态分发服务端口与认证令牌(Token)。
|
||||
* **多隧道反向代理映射**:支持在单个内网客户端上映射多个内网 Web 端口,并将多域名路由绑定至对应的中继节点。
|
||||
* **独立进程生命周期管控**:中继与客户端均为 Go 编写的独立二进制守护进程,内部负责拉起、监控、自愈及热升级底层的 frp 引擎。
|
||||
* **基于 Token 的独立认证隔离**:中继端使用 `agent_token`,内网客户端使用专属 `tunnel_token`,权限与路由边界隔离。
|
||||
* **配置校验与增量热重载**:仅在隧道绑定关系、证书或 Relay 拓扑发生实际变化时,才重写配置文件并平滑重载进程,降低运行开销。
|
||||
|
||||
---
|
||||
|
||||
## 内网穿透与隧道架构
|
||||
|
||||
内网穿透子系统基于成熟的 `frp` 高性能隧道协议进行整合,分为 **控制面 (Control Plane)** 与 **数据面 (Data Plane)**。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
%% 数据流
|
||||
Browser[1. 浏览器 / 访客] -->|HTTPS 请求| Agent[2. OpenResty / Agent]
|
||||
Agent -->|本机转发 proxy_pass| RelayFrps[3. OpenFlare Relay / frps]
|
||||
RelayFrps -->|加密隧道协议| FlaredFrpc[4. OpenFlared / frpc]
|
||||
FlaredFrpc -->|转发本地请求| LocalOrigin[5. 内网源站 192.168.x.x]
|
||||
|
||||
%% 控制流与心跳
|
||||
Server[OpenFlare Server 控制面] <-->|Relay API / Heartbeat| RelayManager[openflare-relay 进程]
|
||||
Server <-->|Client API / Heartbeat| ClientManager[openflared 进程]
|
||||
|
||||
RelayManager -.->|管控进程及配置| RelayFrps
|
||||
ClientManager -.->|管控多 Relay 进程| FlaredFrpc
|
||||
|
||||
style Browser fill:#f9f,stroke:#333,stroke-width:2px
|
||||
style LocalOrigin fill:#9f9,stroke:#333,stroke-width:2px
|
||||
style Server fill:#f96,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
* **控制面(Control Plane)**:Server 维护数据库状态;中继节点上的 `openflare-relay` 进程与内网服务器上的 `openflared` 进程通过 HTTP 心跳与 WebSocket 长通道同步隧道配置。
|
||||
* **数据面(Data Plane)**:公网流量首先进入公网边缘的 Agent (OpenResty),在此完成 HTTPS 握手、TLS 终止和 WAF 过滤,接着通过 `proxy_pass` 转发到同机部署的 `openflare-relay (frps)`。`frps` 再将请求封包通过与内网 `openflared (frpc)` 建立的持久隧道传输过去,最后由 `frpc` 拆包并分发给内网实际的源站服务。
|
||||
|
||||
---
|
||||
|
||||
## Relay (中继端) 设计
|
||||
|
||||
`openflare-relay` 是部署在公网边缘的中继管理器,运行在 `tunnel_relay` 类型的节点上。
|
||||
|
||||
### 1. 核心架构与逻辑
|
||||
* **进程守护**:Relay 进程内部持有 `frps` 二进制,通过 `exec.Command` 拉起 `frps -c frps.toml` 子进程,并启动 goroutine 异步监听其退出状态。如果发现 `frps` 异常退出,会结合退避机制自动拉起。
|
||||
* **动态配置渲染**:通过 HTTP 心跳向控制面同步状态,获取当前的 `RelayConfig`,主要参数包括:
|
||||
* `bindPort`:frps 用于监听内网 frpc 客户端连接的公网控制端口。
|
||||
* `vhostHTTPPort`:虚拟主机(Virtual Host)HTTP 流量监听端口,Agent 的 proxy_pass 会指向此端口。
|
||||
* `authToken`:客户端连接时进行握手校验的安全凭证。
|
||||
* `webServer`:开启 frps 的仪表盘 API,Relay 基于此接口或管理控制端口收集实时的活跃隧道数和流量指标。
|
||||
* **状态上报**:Relay 每周期心跳会向控制面上报底层 `frps` 的活跃连接数、注册客户端数、各个代理通道的实时状态以及 Relay 版本。
|
||||
|
||||
---
|
||||
|
||||
## Openflared (客户端) 设计
|
||||
|
||||
`openflared` 是运行在用户内网服务器侧的客户端管理器,使用独立的 `tunnel_token` 进行鉴权。
|
||||
|
||||
### 1. 核心设计机制
|
||||
* **多 Relay 支持(多路复用)**:
|
||||
为保障高可用或就近接入,控制面可能会将客户端连接调度到多个公网 Relay。`openflared` 会读取 `TunnelConfig` 中下发的 Relays 列表,在本地为每一个 Relay 节点独立生成一个专用的配置文件(命名为 `frpc_<relay_node_id>.toml`),并分别为每个 Relay 进程分配独立的 cancelable context。
|
||||
* **子进程独立监控**:
|
||||
`openflared` 内部维护一个 `processes` 映射表,对每个 `frpc` 子进程进行独立的生命周期管控。当控制面增加或移除 Relay 时,客户端会增量拉起新进程或优雅注销老进程,避免影响其他正常工作的隧道。
|
||||
* **动态 TOML 生成**:
|
||||
为每个 Relay 渲染 TOML 时,客户端会遍历 Proxies 列表,将每个内网服务的 `LocalAddr`、`LocalPort`、绑定的 `CustomDomains` 写入到 `[[proxies]]` 块中。
|
||||
|
||||
---
|
||||
|
||||
## 交互逻辑与流量模型
|
||||
|
||||
内网穿透子系统实现了一致性版本控制和状态反馈。
|
||||
|
||||
### 1. 控制面发布与同步流程
|
||||
|
||||
```text
|
||||
管理员修改隧道/内网端口映射 -> 提交发布 -> 生成新 Tunnel 版本与 Checksum
|
||||
|
|
||||
v (推送或心跳拉取)
|
||||
+-------------------------------------------+-------------------------------------------+
|
||||
| |
|
||||
v (中继端) v (内网客户端)
|
||||
openflare-relay 心跳检测到 frps 端口/Token 变化 openflared 心跳检测到 tunnel_version 发生变更
|
||||
重新渲染本地 frps.toml 请求拉取最新代理映射包
|
||||
Kill 并重新拉起 frps 进程 重新渲染 frpc_<relay_id>.toml
|
||||
上报健康状态为 healthy 对有变更的 Relay 进程执行重启与配置热重载
|
||||
上报应用结果 (Apply Success/Error)
|
||||
```
|
||||
|
||||
1. **版本化控制**:所有内网隧道的路由和映射关系与主路由系统类似,也经过版本化控制,下发 `version` 与 `checksum`,确保客户端不重复写入和频繁重载进程。
|
||||
2. **应用结果闭环**:客户端应用新配置后,会在心跳中携带应用结果上报控制面。若因内网端口不可达或证书配置有误导致 frpc 无法建连,客户端会截获进程输出将 `LastError` 上报,管理员在 Server 即可直观查看穿透失败原因。
|
||||
|
||||
### 2. 数据面流量模型
|
||||
1. **公网入口 (Agent)**:
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name intranet.example.com;
|
||||
# ... TLS 证书与 WAF 过滤逻辑 ...
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:18080; # 指向本地 frps 的虚拟主机端口
|
||||
proxy_set_header Host $host; # 必须保留原 Host,因为 frps 依靠 Host 进行内部路由分发
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
2. **中继节点 (frps)**:
|
||||
`frps` 监听到 `18080` 端口有 HTTP 请求进来,读取 HTTP 请求头中的 `Host: intranet.example.com`,在其已注册的活跃隧道表中检索该域名对应的加密 TCP 连接(由内网 frpc 建立)。
|
||||
3. **加密隧道传输 (TCP)**:
|
||||
`frps` 将 HTTP 请求封装进内部 TCP 隧道协议,发送给内网的 `frpc` 客户端。
|
||||
4. **内网客户端分发 (frpc)**:
|
||||
`openflared` 管理的 `frpc` 收到封包,根据本地配置(`localIP = "127.0.0.1"`, `localPort = 8080`)将请求建立本地 TCP 连接转发给内网 Web 服务,并将 Web 服务的响应原路打包返回,最终呈现给公网用户。
|
||||
@@ -0,0 +1,131 @@
|
||||
# WAF 设计文档
|
||||
|
||||
你会学到:OpenFlare 边缘 Web 应用防火墙(WAF)的核心架构、动态 IP 组异步差分同步模型、OpenResty Lua 高性能缓存方案以及完整的请求过滤与判定逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 需求分析
|
||||
|
||||
在互联网公开环境中,Web 应用程序面临着各种各样的安全威胁(如扫描器踩点、刷接口、针对特定地域的恶意网络爬虫、勒索攻击及 CC 攻击等)。如果直接把恶意请求放行给源站(Origin Server),会导致:
|
||||
1. **源站负载飙升**:高频的数据库查询与 CPU 运算极易耗尽服务器资源。
|
||||
2. **敏感接口被刷**:登录、注册、短信验证码接口容易被恶意滥用导致财产损失。
|
||||
3. **数据泄露风险**:恶意的通用漏洞探测行为无法被提前拦截。
|
||||
|
||||
因此,OpenFlare 需要在最前端的数据面(OpenResty)构建一套 **高性能、可弹性伸缩的 WAF 过滤引擎**。该引擎能够在最接近用户的边缘层以毫秒级的极低开销对恶意请求进行深度过滤,减轻源站压力,并提供防 CC(PoW 挑战)、IP 黑白名单与地域级别拦截等核心安全防护能力。
|
||||
|
||||
---
|
||||
|
||||
## 核心功能
|
||||
|
||||
OpenFlare WAF 包含以下核心防护维度:
|
||||
|
||||
* **IP 级拦截(IP 黑白名单)**:支持单 IP、CIDR 网段过滤,支持将上万 IP 聚合为 IP 组进行高效比对。
|
||||
* **地域黑白名单(GeoIP 限制)**:集成 MaxMind 数据库,支持针对国家(Country)和省份/地区(Region)执行精准准入控制。
|
||||
* **自定义拦截响应**:支持针对不同的过滤规则自定义阻断状态码(如 403, 418)以及个性化的 HTML 拦截页面。
|
||||
* **人机挑战(PoW CC 防护)**:支持无感人机挑战,通过计算 Hash 碰撞防止自动化脚本和僵尸网络(Botnet)对接口进行并发冲击。
|
||||
|
||||
---
|
||||
|
||||
## IP 组设计与动态异步同步
|
||||
|
||||
IP 组是 WAF 进行高效黑白名单管控的核心容器。OpenFlare 将 IP 组根据更新频率与产生渠道分为三类:
|
||||
|
||||
### 1. IP 组类型
|
||||
* **手动 IP 组(Manual)**:由管理员在控制面板上手动输入 IP 或 CIDR 列表。主要用于静态的信任 IP 或长期的封禁。
|
||||
* **订阅 IP 组(Subscription)**:配置远程文本(按行分隔)或标准的 JSON 订阅地址。Server 侧的定时任务会周期性抓取远程订阅源并自动解析导入。主要用于集成开源的威胁情报库、云厂商的 IP 范围等。
|
||||
* **自动 IP 组(Automatic)**:**最具弹性的动态防护通道**。控制面的定时扫描任务会读取所有节点的访问日志,按照设定的 Expr 规则(例如:“5分钟内请求 `/api/login` 接口触发 401 超过 50 次”)进行聚合分析,一旦匹配,自动将该恶意源 IP 写入封禁组,并指定封禁时长。
|
||||
|
||||
### 2. 异步差分同步设计 (不触发 Nginx Reload)
|
||||
在传统的 Nginx WAF 设计中,IP 黑名单的更新通常需要重写配置并 reload。如果恶意 IP 封禁以秒级或分钟级高频触发,频繁 reload 会导致 Nginx 频繁新建 Worker 进程并销毁老进程,导致性能骤降。
|
||||
|
||||
OpenFlare 采用 **动态 IP 组异步差分同步设计**:
|
||||
|
||||
```text
|
||||
WAF IP 成员更新 (手动/订阅/自动自动触发)
|
||||
|
|
||||
v
|
||||
Server 更新数据库并计算该 IP 组的全新 MD5 Checksum
|
||||
|
|
||||
+----------------------------------------+
|
||||
| (WebSocket 实时广播) | (心跳兜底比对)
|
||||
v v
|
||||
Server 立即向所有 Agent 推送变更组的完整成员 Agent 心跳上报本地所有 IP 组的 Checksum 映射表
|
||||
| |
|
||||
| v
|
||||
| Server 发现 Checksum 不一致,下发变更的 IP 组成员
|
||||
v |
|
||||
Agent 接收成员数据,将其以 JSON 形式写入本地磁盘路径:waf_ip_groups.json
|
||||
|
|
||||
v (Lua 内存感知)
|
||||
OpenResty Lua 引擎通过 MD5 校验和秒级感知文件变化并热更新内存,无需 reload 进程
|
||||
```
|
||||
|
||||
通过这一架构,上万个高频变动的动态黑名单 IP 的落地和生效,**全程无需 reload 任何 Nginx 进程**,极大地保护了网关的高并发性能。
|
||||
|
||||
---
|
||||
|
||||
## 规则组与网站绑定
|
||||
|
||||
* **WAF 规则组(Rule Group)**:WAF 过滤政策的最小逻辑集合。一条规则组内可以包含 IP 黑白名单、IP 组引用、地域限制及防 CC 挑战配置。
|
||||
* **全局规则组(Global)**:当规则组被标记为 `is_global = true` 时,该规则组对节点上托管的**所有网站路由**默认生效。
|
||||
* **网站绑定绑定(Site Binding)**:网站路由(Proxy Route)可以绑定一个或多个非全局规则组。判定时,会执行 `全局规则组 + 绑定规则组` 的并集逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 实现方案与高性能缓存
|
||||
|
||||
WAF 在 OpenResty 的 `access_by_lua` 阶段被触发,核心由 Lua 文件与本地落地的 JSON 配置构成。
|
||||
|
||||
### 1. 物理结构
|
||||
* `waf_config.json`:包含所有规则组的元数据、国家地域限制、以及网站(Site)与规则组的关联映射。
|
||||
* `waf_ip_groups.json`:包含所有同步下来的 IP 组与对应的 IP 列表。
|
||||
* `waf/runtime.lua`:WAF 规则比对的实际运行时引擎。
|
||||
* `waf/check.lua`:接入层入口,负责包引入与 check() 触发。
|
||||
|
||||
### 2. 共享内存字典 (ngx.shared) 高性能缓存设计
|
||||
在每次 Web 请求进来时都读取磁盘上的 JSON 文件并进行解码,会导致磁盘 I/O 成为严重的性能瓶颈。
|
||||
|
||||
OpenFlare 利用 **OpenResty 共享内存字典 (ngx.shared.openflare_waf_config)** 设计了二级缓存机制:
|
||||
|
||||
1. **零文件 I/O 路径**:
|
||||
在 Lua 中,每次执行 `check()` 时,首先利用 `ngx.md5` 瞬间计算本地磁盘 JSON 文件的 MD5 哈希(这一操作几乎为零耗时,因为文件已被操作系统 Page Cache 缓存)。
|
||||
2. **哈希比对与热加载**:
|
||||
比对共享内存中存储的缓存哈希键(`_config_hash`)。
|
||||
* **若哈希未发生变化**:直接从共享内存字典中读取已解码、存在内存中的 Lua Table 配置,整个校验过程完全基于**共享内存操作**,耗时在 **微秒级** 级别。
|
||||
* **若哈希不一致**:说明 Agent 刚刚落地了新的 WAF 规则或 IP 组,Lua 自动读取磁盘文件并使用 `cjson.decode` 解码,解码后的数据及全新的 MD5 写入共享内存,供后续 Worker 进程无缝读取。
|
||||
|
||||
---
|
||||
|
||||
## 应用流程与判定判定控制逻辑
|
||||
|
||||
当一个 HTTP/HTTPS 请求到达 OpenResty 后,WAF 会在 `access` 阶段按下图所示的漏斗判决链进行逐步匹配拦截:
|
||||
|
||||
### 1. WAF 判定流程图
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[请求进入 access 阶段] --> B[获取当前请求的 Site Name]
|
||||
B --> C[在共享内存中加载与此 Site 绑定的所有活跃规则组]
|
||||
C --> D{匹配到 IP 白名单 / 白名单 IP 组?}
|
||||
D -- 是 (匹配成功) --> E[放行请求 - ALLOW]
|
||||
D -- 否 --> F{匹配到国家/地区地域白名单?}
|
||||
F -- 是 (匹配成功) --> E
|
||||
F -- 否且已配置任意白名单 --> H
|
||||
F -- 否且未配置白名单 --> G{匹配到 IP 黑名单 / 黑名单 IP 组?}
|
||||
G -- 是 (匹配成功) --> H[阻断请求 - BLOCK]
|
||||
G -- 否 --> I{匹配到国家/地区地域黑名单?}
|
||||
I -- 是 (匹配成功) --> H
|
||||
I -- 否 --> J{是否启用了防 CC PoW 验证?}
|
||||
J -- 是 --> K[转交防 CC 模块处理]
|
||||
J -- 否 --> L[无安全风险,正常放行]
|
||||
|
||||
H --> M[退出并返回规则组配置的自定义状态码与拦截响应体]
|
||||
```
|
||||
|
||||
### 2. 判决步骤细则
|
||||
1. **白名单前置**:
|
||||
为了防止误杀以及保障核心回源流量(如搜索引擎蜘蛛、CDN 回源 IP、办公区出口)的顺畅,WAF **优先匹配 IP 白名单与地域白名单**。一旦白名单匹配成功,直接绕过后续的所有黑名单检测和 CC 挑战,立刻放行。只要当前生效规则组配置了任意白名单,请求未命中全部白名单时会被拦截,白名单在这种情况下表现为准入名单。
|
||||
2. **黑名单强力阻断**:
|
||||
如果在白名单判定中未被捕获,请求将进入黑名单漏斗。一旦请求源 IP 命中 IP 黑名单、命中引用的黑名单 IP 组、或是处于被禁止的国家/地区范围内,Lua 引擎立即将 `ngx.ctx.openflare_waf_blocked` 标记设为 `true`。
|
||||
3. **输出响应**:
|
||||
命中黑名单后,Lua 提取匹配到规则组的 `block_status_code`(默认返回 418 / 403)和 `block_response_body`(拦截页面 HTML),通过 `ngx.say()` 输出响应体并执行 `ngx.exit(status)` 平滑退出请求,防止请求继续向后透传。
|
||||
+55
-16
@@ -2,7 +2,7 @@ import { defineAdditionalConfig, type DefaultTheme } from 'vitepress'
|
||||
|
||||
export default defineAdditionalConfig({
|
||||
description:
|
||||
'OpenFlare is a lightweight, self-hosted OpenResty control plane for reverse proxy rules, releases, node sync, TLS certificates, and basic observability.',
|
||||
'OpenFlare is a lightweight, self-hosted OpenResty control plane for managing reverse proxy rules, configuration publishing, node synchronization, TLS certificates, and basic observability.',
|
||||
|
||||
themeConfig: {
|
||||
nav: nav(),
|
||||
@@ -19,9 +19,37 @@ export default defineAdditionalConfig({
|
||||
},
|
||||
|
||||
footer: {
|
||||
message: 'Released under the Apache License 2.0.',
|
||||
message: 'Released under the Apache License 2.0',
|
||||
copyright: 'Copyright © OpenFlare contributors'
|
||||
}
|
||||
},
|
||||
|
||||
docFooter: {
|
||||
prev: 'Previous Page',
|
||||
next: 'Next Page'
|
||||
},
|
||||
|
||||
outline: {
|
||||
label: 'On this page'
|
||||
},
|
||||
|
||||
lastUpdated: {
|
||||
text: 'Last updated at'
|
||||
},
|
||||
|
||||
notFound: {
|
||||
title: 'Page Not Found',
|
||||
quote: 'This document does not have a corresponding page yet.',
|
||||
linkLabel: 'Go to Home',
|
||||
linkText: 'Back to OpenFlare Docs'
|
||||
},
|
||||
|
||||
langMenuLabel: 'Language',
|
||||
returnToTopLabel: 'Back to top',
|
||||
sidebarMenuLabel: 'Menu',
|
||||
darkModeSwitchLabel: 'Theme',
|
||||
lightModeSwitchTitle: 'Switch to light theme',
|
||||
darkModeSwitchTitle: 'Switch to dark theme',
|
||||
skipToContentLabel: 'Skip to content'
|
||||
}
|
||||
})
|
||||
|
||||
@@ -40,11 +68,14 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
|
||||
items: [
|
||||
{ text: 'Overview', link: '' },
|
||||
{ text: 'Quick Start', link: 'quick-start' },
|
||||
{ text: 'Deployment', link: 'deployment' },
|
||||
{ text: 'Run Server', link: 'server' },
|
||||
{ text: 'Connect Agent', link: 'agent' },
|
||||
{ text: 'Publish First Site', link: 'first-site' },
|
||||
{ text: 'Upgrade and Maintenance', link: 'upgrade' }
|
||||
{ text: 'Basic Usage', link: 'usage' },
|
||||
{ text: 'Tunnel & Intranet Penetration', link: 'tunnel-usage' },
|
||||
{ text: 'WAF Security Protection', link: 'waf-usage' },
|
||||
{ text: 'WAF Auto IP Group Expressions', link: 'waf-ip-group-expr' },
|
||||
{ text: 'SSO Login Configuration', link: 'sso' },
|
||||
{ text: 'Publish First Configuration', link: 'first-site' },
|
||||
{ text: 'Troubleshooting', link: 'troubleshooting' },
|
||||
{ text: 'Credits', link: 'credits' }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -56,10 +87,16 @@ function sidebarReference(): DefaultTheme.SidebarItem[] {
|
||||
text: 'Reference',
|
||||
items: [
|
||||
{ text: 'Overview', link: '' },
|
||||
{ text: 'Configuration', link: 'configuration' },
|
||||
{ text: 'Commands and Scripts', link: 'cli' },
|
||||
{ text: 'API Conventions', link: 'api' },
|
||||
{ text: 'Repository Layout', link: 'repository' }
|
||||
{ text: 'System Architecture', link: '../design/architecture' },
|
||||
{ text: 'Launch Server', link: '../deployment/server' },
|
||||
{ text: 'Access Agent', link: '../deployment/agent' },
|
||||
{ text: 'Deployment Guide', link: '../deployment/deployment' },
|
||||
{ text: 'Deploy Relay (Tunnel)', link: '../deployment/relay' },
|
||||
{ text: 'Deploy OpenFlared', link: '../deployment/openflared' },
|
||||
{ text: 'Upgrade & Maintenance', link: '../deployment/upgrade' },
|
||||
{ text: 'Configuration Options', link: 'configuration' },
|
||||
{ text: 'CLI Commands', link: 'cli' },
|
||||
{ text: 'API Conventions', link: 'api' }
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -70,10 +107,12 @@ function sidebarDesign(): DefaultTheme.SidebarItem[] {
|
||||
{
|
||||
text: 'Design',
|
||||
items: [
|
||||
{ text: 'Product Boundary', link: '' },
|
||||
{ text: 'Architecture', link: 'architecture' },
|
||||
{ text: 'Release Model', link: 'release-model' },
|
||||
{ text: 'Development Constraints', link: 'development' }
|
||||
{ text: 'Product Boundaries', link: '' },
|
||||
{ text: 'System Architecture', link: 'architecture' },
|
||||
{ text: 'Agent & Publish Model', link: 'agent-design' },
|
||||
{ text: 'Tunnel & Intranet Penetration', link: 'tunnel-design' },
|
||||
{ text: 'WAF Design', link: 'waf-design' },
|
||||
{ text: 'Repository Structure', link: 'repository' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# Access Agent
|
||||
|
||||
You will learn: The responsibilities of the Agent, the difference between the two access Tokens, installation script parameters, `agent.json` settings, and how to verify that the node has successfully connected.
|
||||
|
||||
The OpenFlare Agent runs on the proxy node. It does not receive arbitrary remote shell commands; instead, it pulls the configuration version published by the control plane via the Agent API, writes files for OpenResty locally, executes configuration validation, reloads, and attempts to roll back to a working configuration if it fails.
|
||||
|
||||
## Connection Credentials
|
||||
|
||||
| Method | Applicable Scenario |
|
||||
| --- | --- |
|
||||
| `discovery_token` | Automatically registers a node for the first time, which the Server exchanges for a node-specific credential |
|
||||
| `agent_token` | Node has already been created/allocated in the management console, directly uses this node-specific credential |
|
||||
|
||||
At least one of `agent_token` or `discovery_token` must be configured.
|
||||
|
||||
### Credential Retrieval Path
|
||||
|
||||
- **`discovery_token` (Auto Registration Token)**: Log into the management console, navigate to "System Settings" -> "Auto Registration", where you can generate, view, and copy the global auto-registration credential.
|
||||
- **`agent_token` (Node Specific Token)**: Log into the management console, navigate to "Node Management" -> "Add Node", fill in basic node information, save, and copy the node-specific access Token in the node details.
|
||||
|
||||
## One-Click Installation
|
||||
|
||||
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 installation script downloads the latest Agent, writes to `/opt/openflare-agent` by default, generates `agent.json`, and registers `openflare-agent.service` on Linux + systemd environments.
|
||||
|
||||
Supported arguments:
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `--server-url` | Server address (required) | |
|
||||
| `--discovery-token` | One-time auto-registration Token | |
|
||||
| `--agent-token` | Node-specific Token | |
|
||||
| `--install-dir` | Target installation directory | `/opt/openflare-agent` |
|
||||
| `--openresty-path` | Path to the OpenResty binary; automatically detects `openresty` if unspecified | |
|
||||
| `--repo` | GitHub repository to download from | `Rain-kl/OpenFlare` |
|
||||
| `--no-service` | Do not register systemd service | |
|
||||
|
||||
## Configuration File
|
||||
|
||||
Default configuration file path:
|
||||
|
||||
```text
|
||||
/opt/openflare-agent/agent.json
|
||||
```
|
||||
|
||||
Example local configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_path": "openresty",
|
||||
"openresty_observability_port": 18081,
|
||||
"observability_replay_minutes": 15,
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
Example customized OpenResty paths configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "/var/lib/openflare-agent",
|
||||
"openresty_path": "/usr/local/openresty/nginx/sbin/openresty",
|
||||
"main_config_path": "/var/lib/openflare-agent/etc/nginx/nginx.conf",
|
||||
"route_config_path": "/var/lib/openflare-agent/etc/nginx/conf.d/openflare_routes.conf",
|
||||
"access_log_path": "/var/lib/openflare-agent/var/log/openflare/access.log",
|
||||
"cert_dir": "/var/lib/openflare-agent/etc/nginx/certs",
|
||||
"lua_dir": "/var/lib/openflare-agent/etc/nginx/lua",
|
||||
"runtime_config_dir": "/var/lib/openflare-agent/etc/openflare",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
If `openresty_path` is not configured, the Agent calls `openresty` by default. For the full fields, see [Configurations Reference](../reference/configuration.md#agent-configurations-fields).
|
||||
|
||||
## Running in Docker
|
||||
|
||||
For Docker deployments, run the Agent image containing built-in OpenResty directly:
|
||||
|
||||
```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 \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
## Start & Validate
|
||||
|
||||
In a systemd environment:
|
||||
|
||||
```bash
|
||||
systemctl start openflare-agent
|
||||
systemctl status openflare-agent
|
||||
journalctl -u openflare-agent -f
|
||||
```
|
||||
|
||||
Manual execution:
|
||||
|
||||
```bash
|
||||
/opt/openflare-agent/openflare-agent -config /opt/openflare-agent/agent.json
|
||||
```
|
||||
|
||||
Running from source:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
export LOG_LEVEL='info'
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
Running compiled binary:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
Confirm in the management console:
|
||||
|
||||
| Position | Expected Result |
|
||||
| --- | --- |
|
||||
| Node List | Node status is online |
|
||||
| Node Details | Heartbeat, current version, and basic resource metrics display correctly |
|
||||
| Apply Logs | Application result displays after publishing |
|
||||
|
||||
## Uninstall
|
||||
|
||||
To completely uninstall the Agent and wipe local data:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
Supported arguments:
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `--install-dir` | Installation directory | `/opt/openflare-agent` |
|
||||
| `--service-name` | systemd service name | `openflare-agent` |
|
||||
|
||||
The uninstallation script only removes the Agent service, processes, and installation directory; it does not uninstall OpenResty from the host.
|
||||
|
||||
## Common Questions
|
||||
|
||||
| Symptom | Actions |
|
||||
| --- | --- |
|
||||
| `agent_token and discovery_token cannot both be empty` | Check if at least one Token is configured in `agent.json` |
|
||||
| Node stays offline | Run `curl -I http://your-server:3000` on the Agent node to verify that the Server is reachable |
|
||||
| OpenResty is not running | Review `journalctl -u openflare-agent`, checking that `openresty_path` is executable and ports 80/443 are not bound |
|
||||
| Repeated application failures after publishing | The Agent blocks repeated sync attempts of the same failing `version + checksum`; fix the configuration and republish, or activate an older version to roll back |
|
||||
@@ -0,0 +1,287 @@
|
||||
# Deployment Guide
|
||||
|
||||
You will learn: The recommended deployment strategies for OpenFlare, the system requirements for Server and Agent, how to run from source, integration steps, upgrades, and uninstallation entrypoints.
|
||||
|
||||
In production environments, we highly recommend using PostgreSQL as the Server database and explicitly configuring `SESSION_SECRET` for the Server. The recommended Agent deployment method is Docker (which runs the Agent image containing built-in OpenResty); host systemd service installation via script and manual local run are also supported.
|
||||
|
||||
## Deployment Topology
|
||||
|
||||
### Standard Reverse Proxy Traffic Path
|
||||
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
v
|
||||
OpenFlare Server :3000
|
||||
|
|
||||
| Agent API / heartbeat / config pull
|
||||
v
|
||||
OpenFlare Agent
|
||||
|
|
||||
v
|
||||
OpenResty binary
|
||||
|
|
||||
v
|
||||
Origin service
|
||||
```
|
||||
|
||||
### Intranet Penetration Traffic Path
|
||||
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
v
|
||||
OpenResty (Agent, WAF/HTTPS Termination) <-- TunnelRelay Node
|
||||
|
|
||||
| proxy_pass (127.0.0.1:{vhost_port})
|
||||
v
|
||||
OpenFlareRelay (frps process) <-- TunnelRelay Node
|
||||
|
|
||||
| frp tunnel protocol
|
||||
v
|
||||
OpenFlared (frpc client) <-- Intranet Server
|
||||
|
|
||||
v
|
||||
Internal Service (192.168.x.x)
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Server:
|
||||
|
||||
| Item | Requirement |
|
||||
| --- | --- |
|
||||
| Go | `1.25+`, required only when running from source |
|
||||
| Node.js | `18+`, required only when building the admin frontend from source |
|
||||
| Database | Writable SQLite parent directory, or a reachable PostgreSQL instance |
|
||||
| Port | Listens on port `3000` by default |
|
||||
|
||||
Agent:
|
||||
|
||||
| Item | Requirement |
|
||||
| --- | --- |
|
||||
| System | The installation script supports Linux and macOS; the systemd service is created only on Linux + systemd environments |
|
||||
| Architecture | `amd64` or `arm64` |
|
||||
| OpenResty | Required to have the `openresty` executable when deploying locally, or specify its path via `--openresty-path` |
|
||||
| Docker | Required only when deploying the Agent via Docker image |
|
||||
| Network | The Agent node must be able to reach the Server address |
|
||||
| GeoIP | WAF regional rules rely on the Agent's local MaxMind mmdb; the Agent initializes a built-in library on startup and updates it periodically |
|
||||
|
||||
### Hardware Allocation Recommendations
|
||||
|
||||
| Component | Minimum Allocation | Recommended Allocation | Note |
|
||||
| --- | --- | --- | --- |
|
||||
| **Server Control Plane** | 1 Core CPU / 1 GB RAM / 10 GB Disk | 2 Cores CPU / 4 GB RAM / 50 GB+ Disk | Expand disk allocation according to log retention windows and concurrency. |
|
||||
| **Agent Data Plane** | 1 Core CPU / 512 MB RAM / 2 GB Disk | 2 Cores CPU / 2 GB RAM / 10 GB+ Disk | Expand according to concurrent reverse proxy connections and WAF workloads. |
|
||||
| **Relay Node** | 1 Core CPU / 1 GB RAM / 5 GB Disk | 2 Cores CPU / 2 GB RAM / 20 GB Disk | frps throughput is primarily bounded by CPU processing capacity and bandwidth. |
|
||||
| **OpenFlared Client** | 1 Core CPU / 256 MB RAM / 1 GB Disk | 1 Core CPU / 512 MB RAM / 5 GB Disk | Runs inside the intranet; utilizes minimal CPU/RAM, optimize for network throughput. |
|
||||
|
||||
## Docker Compose Deployment for Server
|
||||
|
||||
Create a `docker-compose.yml` file:
|
||||
|
||||
```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
|
||||
container_name: openflare
|
||||
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 Server:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose ps
|
||||
docker compose logs -f openflare
|
||||
```
|
||||
|
||||
Access `http://localhost:3000` for the first time, using the default credentials `root` / `123456`. Please change the default password immediately after logging in.
|
||||
|
||||
## Start Server from Source
|
||||
|
||||
First, build the admin frontend:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Then, launch the Server:
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export SESSION_SECRET='replace-with-a-long-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
# Optional: Prefer PostgreSQL by setting DSN
|
||||
# export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
go run .
|
||||
```
|
||||
|
||||
By default, the Server listens on port `3000`. You can also specify it explicitly:
|
||||
|
||||
```bash
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
## Running Agent in Docker (Recommended)
|
||||
|
||||
Docker is the recommended deployment method for the Agent. Running the Agent image directly launches the Agent controller alongside the built-in OpenResty binary. If `node_ip` is left blank, the Agent automatically resolves its outbound public IP via third-party APIs, avoiding registering the Docker bridge address as the node IP.
|
||||
|
||||
Mounting the configuration file:
|
||||
|
||||
```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 \
|
||||
-v ./agent.json:/etc/openflare/agent.json:ro \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
Using environment variables:
|
||||
|
||||
```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 \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
ghcr.io/rain-kl/openflare-agent:latest
|
||||
```
|
||||
|
||||
## Agent Connection via Installation Script
|
||||
|
||||
Apart from Docker, you can deploy the Agent directly on a Linux/macOS host using the installation script.
|
||||
|
||||
Auto-register using `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
|
||||
```
|
||||
|
||||
Connect using 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
|
||||
```
|
||||
|
||||
Installation script arguments:
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `--server-url` | Server address (required) | |
|
||||
| `--discovery-token` | Auto-registration Token; mutually exclusive with `--agent-token` | |
|
||||
| `--agent-token` | Node-specific Token; mutually exclusive with `--discovery-token` | |
|
||||
| `--install-dir` | Target installation directory | `/opt/openflare-agent` |
|
||||
| `--openresty-path` | Path to the OpenResty binary; automatically detects `openresty` if unspecified | |
|
||||
| `--repo` | GitHub repository to download from | `Rain-kl/OpenFlare` |
|
||||
| `--no-service` | Do not register systemd service | |
|
||||
|
||||
Confirm service status:
|
||||
|
||||
```bash
|
||||
systemctl status openflare-agent
|
||||
journalctl -u openflare-agent -f
|
||||
```
|
||||
|
||||
## Running the Agent Manually
|
||||
|
||||
Running from source:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
export LOG_LEVEL='info'
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
Running compiled binary:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
Minimal `agent.json` example:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_path": "openresty",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
If `openresty_path` is left blank, the Agent calls `openresty` by default.
|
||||
|
||||
By default, the Agent attempts to upgrade the HTTP heartbeat connection to WebSocket once successfully registered. Once upgraded, configuration activations on the Server notify the Agent instantly; if WebSocket disconnects or fails to establish, the Agent gracefully falls back to HTTP polling.
|
||||
|
||||
WAF geographical filtering depends on the local `GeoLite2-Country.mmdb`. The Agent automatically writes the built-in database to `data_dir/etc/openflare/GeoLite2-Country.mmdb` on startup and checks for periodic updates. Muted warnings are logged if updates fail, having no impact on Nginx configuration sync or reloads.
|
||||
|
||||
## Upgrades & Uninstallation
|
||||
|
||||
Server:
|
||||
|
||||
* Root users can check and trigger Server upgrades in the top header of the management console.
|
||||
* To deploy preview releases, manually check the GitHub Releases page.
|
||||
* You can also trigger upgrades by uploading the compiled Server binary in the console.
|
||||
|
||||
Agent:
|
||||
|
||||
* By default, the Agent automatically upgrades following stable releases.
|
||||
* Agent self-updates require the GitHub Release to contain the compiled binary and a matching `.sha256` checksum file; updates are blocked if the downloaded binary fails the SHA-256 validation.
|
||||
* You can re-execute the installation script to redeploy or force-update the Agent.
|
||||
* Upgrading to preview releases requires a manual trigger.
|
||||
|
||||
Uninstalling the Agent:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
The uninstallation script stops the Agent process, removes the systemd service unit, and wipes the installation directory, without uninstalling OpenResty from the host.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Deployment & Upgrade
|
||||
|
||||
This section provides detailed deployment guides, configuration instructions, and upgrade maintenance procedures for the OpenFlare Server, Agent, Relay, and the OpenFlared client.
|
||||
|
||||
## Content Navigation
|
||||
|
||||
### Quick Start
|
||||
* **[Quick Start](../guide/quick-start.md)**: Start the Server and your first Agent in under 5 minutes using Docker Compose (recommended for new users).
|
||||
|
||||
### Server Deployment
|
||||
* **[Launch Server](./server.md)**: Learn how to build the frontend from source, start the Server, and choose between SQLite or PostgreSQL.
|
||||
|
||||
### Agent Deployment
|
||||
* **[Deploy Agent](./agent.md)**: Explore Agent connection methods, Docker deployment, host script installation, config files, and troubleshooting.
|
||||
|
||||
### Tunnel Intranet Penetration Deployment
|
||||
* **[Deploy Relay](./relay.md)**: View config descriptions, Docker deployment, and host runtime guides for TunnelRelay nodes.
|
||||
* **[Deploy OpenFlared](./openflared.md)**: Access config descriptions, Docker runtime, and auto-sync mechanisms for the intranet client.
|
||||
|
||||
### Upgrade & Maintenance
|
||||
* **[Upgrade & Maintenance](./upgrade.md)**: Discover upgrading procedures for Server/Agent, data retention rules, and validation commands.
|
||||
|
||||
### Reference Manuals
|
||||
* **[Deployment Guide](./deployment.md)**: Browse deployment topologies, prerequisites, Docker Compose samples, and multiple deployment strategies.
|
||||
@@ -0,0 +1,119 @@
|
||||
# Deploy OpenFlared Client
|
||||
|
||||
You will learn: The responsibilities of the OpenFlared client, configuration parameters and environment variables, how to run the client via Docker, and how to deploy the client on an intranet server using the compiled host binary.
|
||||
|
||||
**OpenFlared** is a tunnel client deployed in the user's intranet environment (LANs, private VPCs, or other environments that cannot be directly accessed from the public internet). Its core responsibility is to establish communication with the control plane (OpenFlare Server) via the `X-Tunnel-Token` header, automatically spawning and managing one or more **frpc (Fast Reverse Proxy Client)** subprocesses locally to securely and stably tunnel HTTP traffic back to public relay nodes.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Retrieve Tunnel Token**: Create a new tunnel instance on the "Intranet Penetration" or "Tunnel Management" page in the OpenFlare management console; the system will automatically generate a unique `tunnel_id` and a `tunnel_token` (e.g., `tun-<32hex>`).
|
||||
2. **Outbound Network Permissions**: The intranet server does not require any inbound public IPs or port mappings, but it must be able to reach the **OpenFlare Server URL** and the corresponding **TunnelRelay node control port (default 7000)** over the outbound network.
|
||||
3. **Software Dependencies** (Host deployment only):
|
||||
- You must have an executable `frpc` binary locally (recommended version `v0.61.0+` or the latest stable `v0.69.0`), or specify its path explicitly in the configuration.
|
||||
|
||||
---
|
||||
|
||||
## Configuration & Environment Variables
|
||||
|
||||
`openflared` reads `flared.json` in the working directory by default on startup. Overriding options via environment variables is fully supported.
|
||||
|
||||
### Configuration Fields Details
|
||||
|
||||
| JSON Field | Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | `OPENFLARE_SERVER_URL` | OpenFlare Server API base URL | **None (Required)** |
|
||||
| `tunnel_token` | `OPENFLARE_TUNNEL_TOKEN` | Tunnel client dedicated access Token | **None (Required)** |
|
||||
| `frpc_path` | `OPENFLARE_FRPC_PATH` | Path to the `frpc` executable binary | `"frpc"` |
|
||||
| `data_dir` | `OPENFLARE_DATA_DIR` | Directory to store local data and generated `frpc_{relayNodeID}.toml` configs | `"./data"` |
|
||||
| `state_path` | - | Path to store local state JSON file (saving the last applied version) | `"{data_dir}/flared-state.json"` |
|
||||
| `heartbeat_interval`| - | Heartbeat reporting interval (ms or Go Duration string) | `10000` (10s) |
|
||||
| `sync_interval` | - | Tunnel config polling interval (ms or Go Duration string) | `30000` (30s) |
|
||||
| `request_timeout` | - | HTTP request timeout duration | `10000` (10s) |
|
||||
|
||||
---
|
||||
|
||||
## Docker Deployment (Recommended)
|
||||
|
||||
Docker is the simplest and safest way to run the client inside the intranet. The official `openflared` image embeds the client controller and `frpc v0.69.0` out of the box, requiring no environment setup.
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/rain-kl/openflared:latest
|
||||
docker rm -f openflared 2>/dev/null || true
|
||||
|
||||
docker run -d --name openflared --restart unless-stopped \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_TUNNEL_TOKEN=YOUR_TUNNEL_TOKEN \
|
||||
-v openflared-data:/app/data \
|
||||
ghcr.io/rain-kl/openflared:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Manual Host Deployment
|
||||
|
||||
If you need to run the client directly on a Linux/macOS/Windows host inside the intranet:
|
||||
|
||||
### 1. Compile the Binary
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go build -o flared ./cmd/flared
|
||||
```
|
||||
|
||||
### 2. Prepare `flared.json`
|
||||
|
||||
Create a `flared.json` configuration file in the same directory as the executable:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server-ip:3000",
|
||||
"tunnel_token": "your-tunnel-auth-token",
|
||||
"frpc_path": "/usr/local/bin/frpc",
|
||||
"data_dir": "./data",
|
||||
"heartbeat_interval": "10s",
|
||||
"sync_interval": "30s"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Start the Service
|
||||
|
||||
```bash
|
||||
export LOG_LEVEL='info'
|
||||
./flared -config ./flared.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Start & Validate
|
||||
|
||||
### 1. Auto-Sync Workflow
|
||||
|
||||
Once started successfully, OpenFlared operates the following workflow:
|
||||
- **Heartbeat & Config Fetching**: Periodically polls `/api/flared/heartbeat` and `/api/flared/config` endpoints to validate the Token and evaluate configuration versions.
|
||||
- **File Rendering**: When a new configuration version (or checksum mismatch) is detected, it pulls the complete tunnel routing rules. If multiple Relays are bound, it renders `frpc_{relayNodeID}.toml` configurations in `data_dir` for each Relay.
|
||||
- **Hot Reload or Restart**: Spawns the corresponding `frpc` subprocesses, or executes `frpc reload` / restart actions when configurations change, ensuring traffic mappings are kept up to date.
|
||||
- **Process Auto-Recovery**: If a local `frpc` tunnel process exits unexpectedly, the master program automatically restarts it after a 5-second backoff penalty.
|
||||
|
||||
### 2. View Logs & Connection Status
|
||||
|
||||
```bash
|
||||
# Docker container logs
|
||||
docker logs -f openflared
|
||||
```
|
||||
|
||||
If running correctly, the logs will show output similar to:
|
||||
```text
|
||||
flared config loaded ...
|
||||
detected frpc version v0.69.0
|
||||
flared process started
|
||||
applying new tunnel config {"version": "...", "checksum": "..."}
|
||||
frpc process missing, starting {"relay_id": "..."}
|
||||
```
|
||||
|
||||
### 3. Verify in the Management Console
|
||||
|
||||
Open the **"Intranet Penetration"** page in the management console:
|
||||
- Check the online status of the corresponding tunnel; it should display green as **"Online"**.
|
||||
- You can inspect which relay nodes the tunnel is connected to, and view the detailed routing configurations of the intranet services.
|
||||
@@ -0,0 +1,126 @@
|
||||
# Deploy Relay (Tunnel Relay)
|
||||
|
||||
You will learn: The responsibilities of a TunnelRelay node, `openflare-relay` configuration parameters and environment variables, how to run the Relay via Docker, and how to build and deploy the Relay from source manually.
|
||||
|
||||
In the OpenFlare intranet penetration architecture, the **TunnelRelay node** plays a key role. Unlike standard Edge Nodes, in addition to running the traditional Agent (managing OpenResty for HTTPS/WAF processing), it co-locates the **Relay (frps tunnel manager)** service, responsible for listening to intranet client (OpenFlared) tunnel connections and relaying traffic.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before deploying a TunnelRelay node, ensure:
|
||||
|
||||
1. **Registered as a TunnelRelay node**: Add a node of type `tunnel_relay` in the OpenFlare management console under "Node Management", and retrieve its node-specific `agent_token` or use the global `discovery_token`.
|
||||
2. **Network Ports**:
|
||||
- Ensure `bindPort` (the port frpc clients connect to, default `7000`) is accessible from the public/intranet client networks.
|
||||
- Ensure `vhostHTTPPort` (the HTTP Vhost port, default `8080`) is free and not bound by other processes, as the Agent routes traffic to frps on this port.
|
||||
3. **Software Dependencies** (Host deployment only):
|
||||
- You must have an executable `frps` binary locally (recommended version `v0.61.0+` or the latest stable `v0.69.0`), or specify its path explicitly in the configuration.
|
||||
|
||||
---
|
||||
|
||||
## Configuration & Environment Variables
|
||||
|
||||
`openflare-relay` reads `relay.json` in the working directory by default on startup. Overriding options via environment variables is fully supported.
|
||||
|
||||
### Configuration Fields Details
|
||||
|
||||
| JSON Field | Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | `OPENFLARE_SERVER_URL` | OpenFlare Server API base URL | **None (Required)** |
|
||||
| `agent_token` | `OPENFLARE_AGENT_TOKEN` | Node-specific Token | Mutually exclusive with below |
|
||||
| `discovery_token` | `OPENFLARE_DISCOVERY_TOKEN` | One-time auto-registration Token | Mutually exclusive with above |
|
||||
| `node_name` | `OPENFLARE_NODE_NAME` | Custom name for the node | Hostname by default |
|
||||
| `node_ip` | `OPENFLARE_NODE_IP` | Outbound/listening IP of the node | Automatically detects real outbound IP |
|
||||
| `frps_path` | `OPENFLARE_FRPS_PATH` | Path to the `frps` executable binary | `"frps"` |
|
||||
| `data_dir` | `OPENFLARE_DATA_DIR` | Directory to store local data and generated `frps.toml` | `"./data"` |
|
||||
| `state_path` | - | Path to store local state JSON file | `"{data_dir}/relay-state.json"` |
|
||||
| `heartbeat_interval`| - | Heartbeat interval (integer ms or Go Duration string) | `10000` (10s) |
|
||||
| `request_timeout` | - | HTTP request timeout duration | `10000` (10s) |
|
||||
|
||||
---
|
||||
|
||||
## Docker Deployment (Recommended)
|
||||
|
||||
Docker is the most convenient way to deploy a TunnelRelay node. The official Docker image embeds the `openflare-relay` controller and `frps v0.69.0` out of the box.
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/rain-kl/openflare-relay:latest
|
||||
docker rm -f openflare-relay 2>/dev/null || true
|
||||
|
||||
docker run -d --name openflare-relay --restart unless-stopped \
|
||||
-p 7000:7000 \
|
||||
-e OPENFLARE_SERVER_URL=http://your-server:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=YOUR_AGENT_TOKEN \
|
||||
-v openflare-relay-data:/var/lib/openflare-relay \
|
||||
ghcr.io/rain-kl/openflare-relay:latest
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> The `-p 7000:7000` option maps the port `frpc` clients connect to. If a custom `relay_bind_port` is configured in the management console, change this port mapping on the host accordingly.
|
||||
|
||||
---
|
||||
|
||||
## Manual Host Deployment
|
||||
|
||||
If you prefer to run the Relay directly on a physical host or VM:
|
||||
|
||||
### 1. Compile the Binary
|
||||
|
||||
```bash
|
||||
cd openflare-relay
|
||||
go build -o openflare-relay ./cmd/relay
|
||||
```
|
||||
|
||||
### 2. Prepare `relay.json`
|
||||
|
||||
Create a `relay.json` configuration file in the same directory as the executable:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "your-relay-node-agent-token",
|
||||
"frps_path": "/usr/local/bin/frps",
|
||||
"data_dir": "./data",
|
||||
"heartbeat_interval": "10s",
|
||||
"request_timeout": "10s"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Start the Service
|
||||
|
||||
```bash
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-relay -config ./relay.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Start & Validate
|
||||
|
||||
### 1. View Process Logs
|
||||
|
||||
```bash
|
||||
# Docker container logs
|
||||
docker logs -f openflare-relay
|
||||
```
|
||||
|
||||
If managed via systemd on Linux, execute:
|
||||
```bash
|
||||
journalctl -u openflare-relay -f
|
||||
```
|
||||
|
||||
### 2. Verify Runtime Status
|
||||
|
||||
Upon starting successfully, the Relay operates as follows:
|
||||
- Sends HTTP heartbeats to register and go online with the control plane.
|
||||
- Retrieves the active frps baseline settings (including `bindPort`, `vhostHTTPPort`, and the auto-generated `auth_token`).
|
||||
- Automatically renders the `data/frps.toml` configuration locally.
|
||||
- Spawns the subprocess `frps -c data/frps.toml`.
|
||||
- If the `frps` process crashes, the Relay automatically restarts it after 2 seconds.
|
||||
|
||||
### 3. Verify in the Management Console
|
||||
|
||||
Log into the management console and navigate to **"Node Management"** to verify:
|
||||
- The TunnelRelay node status is marked as **"Online"**.
|
||||
- The Node Type is correctly displayed as **Relay Node** and the frps status displays as **Healthy**.
|
||||
@@ -0,0 +1,170 @@
|
||||
# Launch Server
|
||||
|
||||
You will learn: How to build the admin frontend from source, start the OpenFlare Server, choose between SQLite or PostgreSQL, and access Swagger.
|
||||
|
||||
OpenFlare Server is a Gin + GORM monolithic control plane, responsible for managing the Admin UI, Admin API, Agent API, configuration rendering, version publishing, data storage, and aggregated queries.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
| Item | Requirement |
|
||||
| --- | --- |
|
||||
| Go | `1.25+` |
|
||||
| Node.js | `18+` |
|
||||
| pnpm | Recommended enabling via `corepack enable` |
|
||||
| Database | SQLite parent directory must be writable, or a reachable PostgreSQL instance |
|
||||
|
||||
In production environments, we highly recommend explicitly configuring `SESSION_SECRET` and prioritizing PostgreSQL.
|
||||
|
||||
## Build the Admin Frontend
|
||||
|
||||
The Go Server hosts static assets located in `openflare-server/web/build`. Before starting the Server from source, build the frontend:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Common frontend quality checks:
|
||||
|
||||
```bash
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
```
|
||||
|
||||
## Start with SQLite
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export SESSION_SECRET='replace-with-a-long-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
go run .
|
||||
```
|
||||
|
||||
By default, the Server listens on port `3000`. Access it at:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
## Start with PostgreSQL
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export SESSION_SECRET='replace-with-a-long-random-string'
|
||||
export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
export LOG_LEVEL='info'
|
||||
go run .
|
||||
```
|
||||
|
||||
If `DSN` is set, it takes precedence over SQLite. When both `DSN` and the legacy `SQL_DSN` exist, `DSN` is prioritized.
|
||||
|
||||
If the target PostgreSQL database is empty and a local SQLite database exists at `SQLITE_PATH`, the Server automatically migrates the SQLite data into PostgreSQL during startup, outputting the migration progress in the logs.
|
||||
|
||||
## Start with Docker
|
||||
|
||||
Deploying with Docker avoids the hassle of setting up local Go and Node.js environments. OpenFlare provides official Dockerfiles and Compose configurations to support independent container startups and multi-service orchestrations.
|
||||
|
||||
### 1. Quick Start via Docker Run (SQLite Example)
|
||||
|
||||
Ensure that a local directory for persisting databases and logs has been created. Run the following command to start the Server:
|
||||
|
||||
```bash
|
||||
# Create local mount directory
|
||||
mkdir -p ./openflare-data
|
||||
|
||||
# Start the container
|
||||
docker run -d \
|
||||
--name openflare-server \
|
||||
-p 3000:3000 \
|
||||
-v $(pwd)/openflare-data:/data \
|
||||
-e SESSION_SECRET='replace-with-a-long-random-string' \
|
||||
-e SQLITE_PATH='/data/openflare.db' \
|
||||
-e GIN_MODE='release' \
|
||||
-e LOG_LEVEL='info' \
|
||||
ghcr.io/rain-kl/openflare:latest
|
||||
```
|
||||
|
||||
Startup parameters:
|
||||
* **`-p 3000:3000`**: Maps port `3000` on the host to port `3000` inside the container.
|
||||
* **`-v $(pwd)/openflare-data:/data`**: Mounts the local directory to `/data` in the container, ensuring that the SQLite database `openflare.db` is not lost when restarting or rebuilding the container.
|
||||
* **`SESSION_SECRET`**: The session signing hash key (required).
|
||||
|
||||
---
|
||||
|
||||
### 2. One-click Startup via Docker Compose (Integrated PostgreSQL)
|
||||
|
||||
We recommend using Docker Compose in production environments to orchestrate an independent PostgreSQL database and establish high-availability relationships.
|
||||
|
||||
Create a `docker-compose.yml` file:
|
||||
|
||||
```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-random-string
|
||||
SQLITE_PATH: /data/openflare.db
|
||||
DSN: postgres://openflare:replace-with-strong-password@postgres:5432/openflare?sslmode=disable
|
||||
GIN_MODE: release
|
||||
LOG_LEVEL: info
|
||||
volumes:
|
||||
- ./openflare-data:/data
|
||||
```
|
||||
|
||||
Start the services:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Compose configuration options:
|
||||
* **`depends_on` and `healthcheck`**: Uses PostgreSQL's health check (`pg_isready`) to ensure that the database is fully initialized and ready before launching the OpenFlare Server, preventing panics from failed database connection attempts on first launch.
|
||||
* **Separated Data Volume Mounts**: PostgreSQL data is mounted under `./postgres-data`, and OpenFlare data and backups are mounted under `./openflare-data`, making backups and maintenance simple.
|
||||
|
||||
## CLI Arguments
|
||||
|
||||
```bash
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `--port` | The port the Server listens to | `3000` |
|
||||
| `--log-dir` | The directory to write logs to | Empty, outputs to stdout |
|
||||
| `--version` | Outputs version and exits | `false` |
|
||||
| `--help` | Outputs help and exits | `false` |
|
||||
|
||||
## First Login
|
||||
|
||||
Default credentials:
|
||||
|
||||
| Username | Password |
|
||||
| --- | --- |
|
||||
| `root` | `123456` |
|
||||
|
||||
Please change the default password immediately after your first login.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Upgrade & Maintenance
|
||||
|
||||
You will learn: How to upgrade the Server and the Agent, how to clean up observability data, and which validation commands to execute before and after maintenance.
|
||||
|
||||
Before upgrading, verify the currently active version, the most recent Agent application results, and your database backup strategy. In production environments, never trigger upgrades while a configuration is being published, during large-scale Agent reconnections, or while database migrations are in progress.
|
||||
|
||||
## Server Upgrade
|
||||
|
||||
Root users can check and trigger stable Server upgrades in the top header of the management console. You can also trigger upgrades by uploading the compiled Server binary in the console.
|
||||
|
||||
To deploy preview releases, manually check the GitHub Releases page. We highly recommend prioritizing stable releases in production environments.
|
||||
|
||||
Verify after upgrading:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs -n 100 openflare
|
||||
```
|
||||
|
||||
If deployed from source, restart the Server and verify that no database migration or startup errors appear in the logs.
|
||||
|
||||
## Agent Upgrade
|
||||
|
||||
Node Agents automatically update following stable releases by default. Upgrading to preview releases requires a manual trigger.
|
||||
|
||||
You can re-execute the installation script to redeploy or force-update the Agent:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Note: Re-executing the current installation script wipes the entire installation directory, including the existing `agent.json`, local states, cached databases, and downloaded binaries. Ensure you have the node Token handy before executing the script.
|
||||
|
||||
Verify after upgrading:
|
||||
|
||||
```bash
|
||||
systemctl status openflare-agent
|
||||
journalctl -u openflare-agent -n 100 --no-pager
|
||||
```
|
||||
|
||||
## Data Maintenance
|
||||
|
||||
The management console's Settings page maintains options for automatic cleanup of observability data:
|
||||
|
||||
| Parameter | Description |
|
||||
| --- | --- |
|
||||
| `DatabaseAutoCleanupEnabled` | Toggles daily automatic cleanup |
|
||||
| `DatabaseAutoCleanupRetentionDays` | Data retention duration in days, minimum 1 day |
|
||||
|
||||
When enabled, the Server cleans up access logs, metrics snapshots, and request reports at 3:00 AM daily.
|
||||
@@ -0,0 +1,181 @@
|
||||
# Agent Design Document
|
||||
|
||||
You will learn: Agent design principles, core functional modules, interaction links with the Server, and how configuration applications are secured and made reliable through immutable version models and the three-stage disaster recovery rollback mechanism.
|
||||
|
||||
---
|
||||
|
||||
## Requirements Analysis
|
||||
|
||||
In distributed reverse proxy and edge security gateway scenarios, the Agent plays a central role in connecting the control plane (Server) and the data plane (OpenResty). Since the Agent runs on the user's actual node server, its design must adhere to the following core security and high-availability requirements:
|
||||
|
||||
1. **Active Pull (Pull Model) instead of Push**: The Server does not hold the SSH keys of the nodes, nor does it actively initiate inbound connections to the nodes. All control directives and configuration updates are actively pulled by the Agent via heartbeats or long-lived connections (WebSockets). This eliminates inbound firewall security risks on the node side and prevents control channels from being hijacked.
|
||||
2. **Minimal Intrusiveness**: The Agent runs as an independent Go binary process. It only interacts with the local OpenResty process through file-based configuration rewriting and signal notifications, without interfering with other system services on the node.
|
||||
3. **Robust Disaster Recovery & Self-Healing**: Since network jitter, disk exhaustion, or erroneous configurations can easily lead to configuration sync failures, the Agent must possess zero-dependency local rollback and self-healing capabilities, strictly preventing a single configuration error from causing a complete node outage.
|
||||
4. **Pure Data and State Landing**: The Agent is only responsible for executing file generation and control intentions rendered by the Server. It does not carry complex control plane duties like business logic validation or multi-tenant authorization, ensuring the node side remains highly efficient and lightweight.
|
||||
|
||||
---
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
The Agent is composed of the following core sub-modules, cooperating to manage its complete lifecycle:
|
||||
|
||||
| Module Name | Directory | Responsibilities |
|
||||
| :--- | :--- | :--- |
|
||||
| **Config Sync** | `sync/` | Pulls full configuration packages, writes files, triggers reloads, and records and reports sync statuses. |
|
||||
| **Heartbeat** | `heartbeat/` | Periodically reports node health and resource metrics to the Server and retrieves the latest active version summary. |
|
||||
| **WebSocket** | `wsclient/` | Maintains a persistent connection with the Server, providing sub-second real-time configuration pushes and commands. |
|
||||
| **OpenResty Control** | `nginx/` | Executes Nginx config validation (`openresty -t`), rewrites, graceful reloads (`reload`), and process auto-start. |
|
||||
| **Local State Store** | `state/` | Persistently records local applied versions, error logs, and buffers unsent observability metrics. |
|
||||
| **Self-Updater** | `updater/` | Listens to Server self-update commands, securely pulls new binary versions, and completes in-place upgrades. |
|
||||
| **Observability** | `observability/` | Collects host CPU/memory/disk and Nginx performance metrics, processes access logs, and uploads them. |
|
||||
| **GeoIP Maintenance** | `geoipdata/` `geoipupdate/` | Maintains and updates the local GeoIP database periodically to support WAF country-level filtering. |
|
||||
|
||||
---
|
||||
|
||||
## Interaction Flows with Server
|
||||
|
||||
The Agent communicates with the control plane through **Token-based Auto-Registration** and a **Dual-channel Heartbeat/WebSocket** system during its lifecycle.
|
||||
|
||||
### 1. Auto-Registration Flow
|
||||
|
||||
If the Agent starts with an empty `access_token` in its local `agent.json`, but has a `discovery_token` configured, it triggers the auto-registration flow:
|
||||
1. The Agent sends a registration request to `/api/agent/register`, carrying a local hardware fingerprint, IP, and hostname.
|
||||
2. After validating the `discovery_token`, the Server generates a unique `NodeID` and a dedicated `AccessToken` (i.e., `agent_token`) in the database and returns them.
|
||||
3. The Agent writes the dedicated Token to its local configuration file, clears the one-time `discovery_token`, and uses the `AccessToken` for all subsequent authenticated communications.
|
||||
|
||||
### 2. Dual-Channel Heartbeat & Sync Mechanism
|
||||
|
||||
* **HTTP Polling (Fallback and Detection)**: The Agent sends POST heartbeat packets at configured `heartbeat_interval` intervals by default. It reports health metrics while retrieving the currently active configuration version summary (Version & Checksum).
|
||||
* **WebSocket Channel (Real-time Communication)**: Upon a successful HTTP heartbeat, the Agent automatically attempts to upgrade the connection to WebSocket (`/api/agent/ws`).
|
||||
* Once the WS connection is established, heartbeats and metrics reporting shift entirely to the WS pipeline, reducing network overhead.
|
||||
* When the Server publishes or activates a new version, it broadcasts a notification to the Agent via WS. The Agent triggers the synchronization flow **immediately** upon receiving the change event, achieving sub-second configuration deployment.
|
||||
* If the WS connection drops due to network issues, the Agent automatically falls back to HTTP polling and uses an exponential backoff algorithm to attempt rebuilding the WS channel.
|
||||
|
||||
### 3. Interaction Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Agent as OpenFlare Agent
|
||||
participant OR as Local OpenResty
|
||||
participant Server as OpenFlare Server
|
||||
|
||||
Note over Agent: First Startup (No AccessToken)
|
||||
Agent->>Server: 1. Auto-registration request (carrying discovery_token)
|
||||
Server-->>Agent: 2. Issue NodeID & dedicated AccessToken (agent_token)
|
||||
Note over Agent: Store Token in local configuration file
|
||||
|
||||
rect rgb(240, 248, 255)
|
||||
Note over Agent, Server: HTTP Fallback & WebSocket Upgrade
|
||||
Agent->>Server: 3. Send HTTP Heartbeat (report system metrics & health)
|
||||
Server-->>Agent: 4. Return ActiveConfig summary & AgentSettings
|
||||
Agent->>Server: 5. Initiate WebSocket upgrade request (/api/agent/ws)
|
||||
Server-->>Agent: 6. Upgrade successful (persistent bi-directional channel)
|
||||
end
|
||||
|
||||
rect rgb(245, 245, 245)
|
||||
Note over Agent, Server: Real-time Configuration Publication
|
||||
Note over Server: Administrator clicks publish config in UI
|
||||
Server->>Agent: 7. Broadcast active config summary via WS (WSMessageTypeActiveConfig)
|
||||
Agent->>Server: 8. Request full configuration details (carrying target Version/Checksum)
|
||||
Server-->>Agent: 9. Return complete configuration snapshot (Nginx configs, certs, WAF rules, etc.)
|
||||
Note over Agent: Backup old files, write new config to local temp path
|
||||
Agent->>OR: 10. Execute config syntax validation (openresty -t)
|
||||
OR-->>Agent: 11. Return validation result (OK)
|
||||
Agent->>OR: 12. Send graceful reload signal (openresty -s reload)
|
||||
Agent->>Server: 13. Report application success status (Apply Log & ActiveVersion)
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Control of OpenResty
|
||||
|
||||
The Agent implements end-to-end closed-loop control of the data plane OpenResty, including configuration rendering, syntax validation, graceful reloading, and exception state capturing:
|
||||
|
||||
### 1. Configuration Layout on Disk
|
||||
|
||||
Upon successful sync, the Agent writes configuration files to `/etc/nginx/openflare-lua/` (or the configured `LuaDir`) according to a strict physical structure:
|
||||
* `nginx.conf`: Main configuration file (replaces absolute path placeholders, configures performance parameters, shared dictionaries, and global server blocks).
|
||||
* `routes.conf`: Route configuration file (generated by the Agent, containing all website server blocks, certificate paths, cache settings, and rate limit directives).
|
||||
* `certs/`: Certificate storage directory (files named as `{cert_id}.crt` and `{cert_id}.key`).
|
||||
* `waf/` and `pow/`: Dedicated Lua runtime scripts required for WAF and CC mitigation.
|
||||
* `waf_config.json` and `waf_ip_groups.json`: Structured rules and IP databases required by the WAF filtering engine.
|
||||
|
||||
### 2. Refined Reload Operations
|
||||
|
||||
1. **Backup Current Config**: Before writing new files, the Agent copies the existing configuration files to a `.backup` directory, keeping a complete rollback snapshot.
|
||||
2. **Write and Replace Placeholders**: Writes the pulled templates, automatically replacing absolute path placeholders (e.g., `__OPENFLARE_LUA_DIR__`) with actual local execution paths.
|
||||
3. **Syntax Validation**: Calls `openresty -t -c <temp_nginx.conf>` to run a strict syntax test.
|
||||
4. **Graceful Reload**: If validation passes, the Agent moves the files to the official paths and executes `openresty -s reload`. If OpenResty is not running, it launches the process.
|
||||
5. **Exception Capture**: If validation or reload fails, the Agent intercepts the standard error output (stderr) and extracts the first 2000 characters of the detailed error log.
|
||||
|
||||
---
|
||||
|
||||
## Publishing & Config Application Model
|
||||
|
||||
OpenFlare discards the fragile mechanism of dynamically patching node configurations, instead using an **immutable configuration version publishing model**.
|
||||
|
||||
```text
|
||||
Edit rules -> Preview / View diff -> Publish -> Generate full configuration version -> Activate version -> Agent pulls -> Local application -> Report result
|
||||
```
|
||||
|
||||
### 1. Core Design Principles
|
||||
|
||||
* **Complete Publication**: Every publication compiles all enabled proxy routes, certificates, and global/custom WAF rules at once, generating a complete version package with a unique `checksum`.
|
||||
* **Version Format**: Uses the `YYYYMMDD-NNN` incremental format, ensuring version histories are intuitive and strictly monotonic.
|
||||
* **Global Single Active Version**: The system supports only one globally `active` configuration version at any given time. Rollbacks do not require reverse patching; they simply transition an older healthy version to the `active` state, and the Agent pulls and applies it.
|
||||
|
||||
### 2. Three-Stage Disaster Recovery & Rollback Mechanism
|
||||
|
||||
If the Agent fails to apply a configuration (or reload fails), it automatically triggers the following three-stage self-healing pipeline:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Config Application Failed] --> B[Stage 1: Attempt Local Backup Recovery]
|
||||
B -- Backup Exists --> C[Write Local Backup Files]
|
||||
C --> D[Run openresty -t Validation]
|
||||
D -- Validation OK --> E[Reload Old Configuration]
|
||||
D -- Validation Failed --> F[Proceed to Stage 2]
|
||||
B -- No Backup --> F[Stage 2: Write Built-in Safe Fallback Config]
|
||||
F --> G[Write fallback nginx.conf: Listen on Port 80 Only]
|
||||
G --> H[Enable stub_status health checks]
|
||||
G --> I[Return 503 for all other routes & block errors]
|
||||
G --> J[Attempt to launch OpenResty to maintain basic survival]
|
||||
J --> K[Proceed to Stage 3]
|
||||
E --> L[Report Apply Warning]
|
||||
K --> M[Block Local Repeated Application of Failed Version]
|
||||
M --> N[Report Apply Error with detailed logs]
|
||||
```
|
||||
|
||||
1. **Stage 1: Local Backup Rollback**
|
||||
* The Agent attempts to restore the main configuration, routes, and certificates from the `.backup` directory.
|
||||
* It runs `openresty -t` validation on the restored backup. If successful, it reloads and reports a `Warning` to the Server (Warning: failed to apply new version, automatically rolled back to the previous healthy version).
|
||||
2. **Stage 2: Built-in Safe Fallback Runtime**
|
||||
* If no local backup exists (e.g., first deployment failed) or if the rollback validation fails, the Agent activates the ultimate self-healing mechanism: writing a **built-in safe fallback configuration**.
|
||||
* **Fallback Configuration Specification**:
|
||||
* Listens only on port `80`, containing no real user reverse proxy routes.
|
||||
* The `/openflare/stub_status` endpoint returns a healthy response, while all other requests uniformly return a `503 Service Unavailable` status code with the fixed response body `OpenFlare: No Valid Configuration`.
|
||||
* It attempts to launch OpenResty with this minimal configuration. This keeps the Nginx process alive, preserving underlying health probes and metric endpoints, preventing containers/pods from being repeatedly killed and restarted by orchestration systems, while keeping sensitive routes secure.
|
||||
3. **Stage 3: Local Configuration Blocking**
|
||||
* The Agent records the failing configuration's `version + checksum` in its local state store blacklist.
|
||||
* Until the control plane activates a new configuration (resulting in a changed `checksum`), the Agent's heartbeat blocks repeated synchronization pulls of this erroneous version, preventing nodes from entering an infinite loop of "heartbeat -> pull failing config -> crash rollback".
|
||||
|
||||
### 3. WAF IP Group Asynchronous Runtime Synchronization
|
||||
|
||||
To prevent highly volatile IP blacklists from triggering frequent full config publications and Nginx reloads (which still incur minor CPU and connection overhead), WAF IP groups are synchronized via an **asynchronous differential sync design**:
|
||||
|
||||
* **Static Publication Snapshot**: The `waf_config.json` generated upon publication only contains the group ID reference mapping (i.e., `ip_whitelist_group_ids` / `ip_blacklist_group_ids`) and does not contain the actual list of IP addresses.
|
||||
* **Heartbeat Differential Check**: The Agent uploads its locally cached IP groups MD5 checksum map in its heartbeat.
|
||||
* **Differential Delivery**: The Server compares checksums and only delivers missing or modified IP groups, which are written directly to `waf_ip_groups.json` on the node without reload.
|
||||
* **WebSocket Real-time Push**: When an administrator updates an IP group, or a threat intelligence subscription successfully pulls, or a security rule triggers a temporary block, the Server immediately broadcasts the IP group update package via WebSocket. The Agent receives and applies it instantly **without Nginx reloads**.
|
||||
|
||||
---
|
||||
|
||||
## Design Constraints
|
||||
|
||||
To protect the security boundary of the data and control plane, Agent development must strictly comply with the following engineering constraints:
|
||||
|
||||
1. **Zero-Privilege Command Execution**: The Server is strictly prohibited from sending any arbitrary shell commands or scripts to the Agent (such as exec/eval). All system control operations (such as start, stop, reload, update) must be hardcoded inside the Agent binary.
|
||||
2. **Strict Token Filtering and Prefix Validation**: Agent requests to the Server must be prefixed with `/api/agent/` and must carry the `X-Agent-Token` header for signature or token verification.
|
||||
3. **Node Autonomy**: The Agent must support complete offline capabilities. During disconnected periods, the local OpenResty must rely on local configuration copies to keep reverse proxy services running normally.
|
||||
+207
-17
@@ -1,33 +1,223 @@
|
||||
# Architecture
|
||||
# System Architecture
|
||||
|
||||
OpenFlare consists of Server, Agent, and local OpenResty on each node.
|
||||
You will learn: The overall architecture of OpenFlare, the boundaries of responsibilities for Server, Agent, OpenResty, and Admin Frontend, and the request flow of a configuration publication from the admin dashboard to activation on a node.
|
||||
|
||||
OpenFlare consists of the Server, the Agent, the node-local OpenResty, and the Admin Frontend. The Server is the control plane, the Agent is the only controlled entry point on the node side, and OpenResty serves as the actual data plane. In intranet penetration scenarios, the Relay (frps manager) and OpenFlared (frpc manager) extend the data plane traffic path.
|
||||
|
||||
### Standard Reverse Proxy Traffic Path
|
||||
|
||||
```text
|
||||
OpenFlare Server (Gin + SQLite/PostgreSQL + Web UI)
|
||||
|
|
||||
| HTTP API / Config Pull
|
||||
v
|
||||
OpenFlare Agent (register / heartbeat / sync / apply / update)
|
||||
|
|
||||
v
|
||||
Local OpenResty or Docker OpenResty
|
||||
|
|
||||
v
|
||||
Browser
|
||||
|
|
||||
| Management UI / API
|
||||
v
|
||||
OpenFlare Server (Gin + GORM + SQLite/PostgreSQL)
|
||||
|
|
||||
| Agent API / heartbeat / config pull
|
||||
v
|
||||
OpenFlare Agent
|
||||
|
|
||||
| write config / openresty -t / reload / rollback
|
||||
v
|
||||
OpenResty binary
|
||||
|
|
||||
| reverse proxy
|
||||
v
|
||||
Origin
|
||||
```
|
||||
|
||||
### Intranet Penetration Traffic Path
|
||||
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
| HTTPS request
|
||||
v
|
||||
OpenResty (Agent, TLS/WAF) <-- TunnelRelay Node
|
||||
|
|
||||
| proxy_pass http://localhost:vhost_port (Host header preserved)
|
||||
v
|
||||
OpenFlareRelay (frps) <-- TunnelRelay Node, co-located with Agent
|
||||
|
|
||||
| frp tunnel protocol (HTTP Vhost routing by Host header)
|
||||
v
|
||||
OpenFlared (frpc) <-- Intranet Server
|
||||
|
|
||||
| HTTP/HTTPS forward
|
||||
v
|
||||
Internal Service (192.168.x.x)
|
||||
```
|
||||
|
||||
## Component Responsibilities
|
||||
|
||||
| Component | Responsibility |
|
||||
| --- | --- |
|
||||
| Server | Admin UI, Admin API, Agent/Relay/Client API, configuration rendering, version publishing, data storage, and aggregated queries. |
|
||||
| Agent | Registration, heartbeats, synchronization, file writing, validation, reload, rollback on failure, self-updating, and light metrics collection. |
|
||||
| OpenResty | Receives real traffic, executing WAF, PoW, authentication, and reverse proxying according to the configuration rendered by OpenFlare. |
|
||||
| OpenFlareRelay | Manages the lifecycle of the frps process, providing tunnel relay services and receiving frps configurations via heartbeat. |
|
||||
| OpenFlared | Manages frpc processes (can be multiple), connecting to the Relay and forwarding traffic to intranet services. |
|
||||
| Frontend | Manages pages for website configs, WAF, origins, certificates, nodes, tunnels, versions, users, settings, and observability. |
|
||||
|
||||
## Server
|
||||
|
||||
`openflare_server` is a monolithic control plane based on Gin, GORM, SQLite/PostgreSQL, the existing login/session system, and the static frontend build.
|
||||
`openflare-server` is the single-control-plane monolith:
|
||||
|
||||
It owns the admin UI and API, Agent API, configuration rendering, version publishing, storage, and aggregate queries.
|
||||
* Gin provides the HTTP services.
|
||||
* GORM accesses SQLite or PostgreSQL.
|
||||
* The existing login system provides Admin Session management.
|
||||
* Authentication sources support GitHub OAuth and standard OIDC logins with external account binding.
|
||||
* The Go Server hosts the `openflare-server/web` static build assets.
|
||||
|
||||
The Server does not directly SSH to nodes, nor does it modify node files online. It only stores control plane state, generates complete configuration versions, and lets nodes actively pull them via the Agent API.
|
||||
|
||||
## Agent
|
||||
|
||||
`openflare_agent` is a single Go binary that runs locally on each node. It prefers `openresty_path` when configured and uses Docker OpenResty by default otherwise.
|
||||
`openflare-agent` is a Go monolithic application:
|
||||
|
||||
It handles registration, heartbeat, sync, file writes, `openresty -t`, reload, rollback, self-update, and lightweight collection.
|
||||
* Runs as a single binary on the node side.
|
||||
* Reads or generates local node information on startup.
|
||||
* Performs periodic heartbeat check-ins to report status and retrieve active version summaries.
|
||||
* Upon discovering a new version, it pulls the configuration, backs up old files, writes new files, validates them, and reloads.
|
||||
* Automatically rolls back to restore operations if the application fails.
|
||||
* Maintains the local WAF GeoIP mmdb, writing the built-in library on startup and updating it periodically based on configuration.
|
||||
|
||||
The Agent executes validation, reload, startup, and restart uniformly via the path specified in `openresty_path`; if unconfigured, it defaults to calling `openresty`. During Docker deployments, the Agent image packages OpenResty and follows the same execution control logic.
|
||||
|
||||
The node IP is maintained by default through Agent registration and heartbeat reporting; if the administrator locks the node IP, the Server only updates running status, versions, and observability fields, and no longer accepts reports from the Agent to override the locked IP.
|
||||
|
||||
## Frontend
|
||||
|
||||
`openflare_server/web` is the production frontend baseline: Next.js App Router, React 19, TypeScript, and Tailwind CSS.
|
||||
`openflare-server/web` is the official Next.js-based frontend:
|
||||
|
||||
* Next.js 15 App Router.
|
||||
* React 19.
|
||||
* TypeScript.
|
||||
* Tailwind CSS.
|
||||
* TanStack Query for server-side state.
|
||||
|
||||
The frontend uses static export mode (`output: 'export'`), which is then hosted by the Go Server using `embed.FS`. All API requests must go through `lib/api/` and process the `success/message/data` response structure.
|
||||
|
||||
The Server integrates the following security features:
|
||||
* CORS middleware: Cross-Origin Resource Sharing protection.
|
||||
* Rate limiting: Global and key API endpoint throttling.
|
||||
* Session management: Cookie/Redis-based session storage.
|
||||
|
||||
## Data & Request Flow
|
||||
|
||||
### Management Request Flow
|
||||
|
||||
```text
|
||||
Browser -> Frontend -> /api/* -> controller -> service -> model -> database
|
||||
```
|
||||
|
||||
Admin mutation APIs use `POST`, while read-only APIs use `GET`. Both success and failure responses return a clear `message`.
|
||||
|
||||
### Agent Sync Flow
|
||||
|
||||
```text
|
||||
Agent HTTP heartbeat -> Server returns active version summary
|
||||
Agent detects new version -> Pulls complete configuration details
|
||||
Agent writes main configuration / route configurations / certificates / Lua resources / WAF runtimes
|
||||
Agent runs OpenResty validation (openresty -t) and reload
|
||||
Agent reports application result
|
||||
```
|
||||
|
||||
### Relay Sync Flow
|
||||
|
||||
The Relay (OpenFlareRelay process) runs on the TunnelRelay node and shares the same `agent_token` with the Agent:
|
||||
|
||||
```text
|
||||
Relay HTTP heartbeat -> Server returns frps base configuration (bindPort, vhostHTTPPort, auth_token)
|
||||
Relay generates frps.toml and starts or updates the frps process
|
||||
Relay periodically reports frps health status and connection statistics
|
||||
Relay attempts WebSocket upgrade for real-time configuration pushes
|
||||
```
|
||||
|
||||
frps configurations are relatively static (ports, auth token), dispatched via heartbeats, and **not included in the versioned publishing flow**. The Relay must monitor the frps process and auto-recover it on failures. Authentication: `X-Agent-Token` + API path prefix `/api/relay/*`, distinguished by Server via `node_type = tunnel_relay`.
|
||||
|
||||
### OpenFlared Sync Flow
|
||||
|
||||
OpenFlared (client) runs inside the intranet server, using independent `tunnel_token` authentication:
|
||||
|
||||
```text
|
||||
Client HTTP heartbeat -> Server returns tunnel configuration version summary (version, checksum)
|
||||
Client detects new version -> Pulls complete tunnel route configuration (relay list + frpc proxy definitions)
|
||||
Client generates independent frpc.toml configuration files for each Relay
|
||||
Client starts a new frpc process for new Relays, or hot-reloads (frpc reload) existing ones
|
||||
Client reports application results (success/failure details)
|
||||
```
|
||||
|
||||
OpenFlared communicates with the Server via `/api/flared/*` using the `X-Tunnel-Token` header. Tunnel route configurations are versioned along with the publishing flow, ensuring all configuration changes are consistently published to both Agents and Clients via a single version number.
|
||||
|
||||
**WebSocket Upgrade Flow** (Optional, controlled via `AgentWebsocketUpgradeEnabled`):
|
||||
|
||||
When WebSocket upgrade is enabled:
|
||||
1. The Agent retrieves run configurations and settings via HTTP heartbeat.
|
||||
2. The Agent attempts to upgrade the connection to `GET /api/agent/ws` (WebSocket).
|
||||
3. Once the WS connection is established, periodic state reporting and real-time commands are carried over the WebSocket pipeline, minimizing latency.
|
||||
4. When the Server publishes or activates a version, it immediately broadcasts the active version summary to connected Agents, triggering the sync flow instantly.
|
||||
5. If the WebSocket disconnects or fails to establish, the Agent automatically falls back to HTTP heartbeats, ensuring high availability.
|
||||
|
||||
Through the `OpenRestyWebsocketEnabled` option, WebSocket reverse proxy support can be enabled or disabled at the OpenResty layer.
|
||||
|
||||
### Reverse Proxy Flow
|
||||
|
||||
```text
|
||||
Client -> OpenResty server block -> WAF Lua -> named upstream -> Origin
|
||||
```
|
||||
|
||||
Website configurations are the boundaries of reverse proxy aggregation. A single website configuration can bind multiple domains, sharing site-level rate limiting, reverse proxy, and cache settings.
|
||||
|
||||
WAF executes in the OpenResty `access_by_lua_file` phase. Rules originate from the `waf_config.json` carried in the currently active version; global rule groups take effect by default, and websites can overlay custom rule groups. `waf_config.json` only stores rule group references and IP group IDs; IP group members are synchronized independently by the Agent into `waf_ip_groups.json`, and the OpenResty Lua engine merges and evaluates them by reference ID.
|
||||
|
||||
WAF IP groups are managed by the Server. Manual IP groups store IP/CIDR lists directly; auto IP groups are evaluated by Server cron jobs reading request logs and applying Expr boolean rules; subscription IP groups are fetched by Server cron jobs from remote text or JSON sources. The Agent reports local IP group checksums in heartbeats, and the Server only returns mismatched IP groups. When an IP group is updated on the Server, a broadcast is sent via WebSocket to push changes, and the OpenResty Lua reads the local JSON file directly without querying the DB, request logs, or remote subscription sources.
|
||||
|
||||
## Core Objects
|
||||
|
||||
Current valid entities include:
|
||||
|
||||
* `proxy_routes`
|
||||
* `origins`
|
||||
* `config_versions`
|
||||
* `nodes`
|
||||
* `tunnels`
|
||||
* `auth_sources`
|
||||
* `external_accounts`
|
||||
* `node_system_profiles`
|
||||
* `apply_logs`
|
||||
* `tls_certificates`
|
||||
* `managed_domains`
|
||||
* `node_request_reports`
|
||||
* `node_access_logs`
|
||||
* `node_metric_snapshots`
|
||||
* `traffic_analytics_rollups`
|
||||
* `node_health_events`
|
||||
* `waf_rule_groups`
|
||||
* `waf_ip_groups`
|
||||
* `waf_rule_group_bindings`
|
||||
* `acme_accounts`
|
||||
* `dns_accounts`
|
||||
* `geoip_update_configs`
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
| Decision | Rationale |
|
||||
| --- | --- |
|
||||
| Full Config Versioning instead of Patches | Provides stable, verifiable boundaries for previewing, activating, history, and rollbacks. |
|
||||
| Pull Model (Agent-driven) | Server does not need SSH keys or inbound command ports, preventing control channel hijacking. Supports HTTP and WebSocket. |
|
||||
| Global Single Active Version | Reduces MVP complexity, ensuring all nodes are uniform by default. Supports previews, version history, and one-click rollback. |
|
||||
| Website Multi-Domain Aggregation | Enables sharing site-level policies across domains while supporting per-domain certificate binding. |
|
||||
| Server-side Observability Aggregation | Prevents UI-side temporary statistical calculations from producing inconsistent data metrics. |
|
||||
| Intranet Penetration based on frp | Reuses a mature tunnel protocol rather than custom implementations to minimize stability risks. frps Vhost routing aligns naturally with HTTP. |
|
||||
| Independent Binary for Relay/Client | Separation of concerns: Relay manages frps, Client manages frpc, allowing independent updates and deployments. |
|
||||
| Tunnel decoupled from Node system | Tunnel clients run internally, using completely different registration and authentication flows compared to edge nodes. |
|
||||
|
||||
## Recommended Reading for Contributors
|
||||
|
||||
Before modifying architectural code, please read:
|
||||
|
||||
1. [Product Boundaries](./index.md)
|
||||
2. [Agent & Publish Model](./agent-design.md)
|
||||
3. [Development Constraints](../../guideline/Constraints.md)
|
||||
4. [Repository Structure](./repository.md)
|
||||
|
||||
+171
-20
@@ -1,31 +1,182 @@
|
||||
# Development Constraints
|
||||
# Local Development
|
||||
|
||||
After `1.0.0`, OpenFlare development prioritizes stability, upgrade and rollback reliability, documentation accuracy, test coverage, and small iterations inside the existing boundary.
|
||||
You will learn: How to build OpenFlare's local development environment, start the Server, the Agent, and the Admin Frontend, run test and build commands, and understand the boundaries to respect before contributing code.
|
||||
|
||||
## Change Admission
|
||||
This page is aimed at contributors. Product boundaries, data model constraints, API conventions, and frontend layering specifications are governed by [Development Constraints](../../guideline/Constraints.md); this page only provides actionable workflows for local development.
|
||||
|
||||
Before implementing a requirement, check:
|
||||
## Repository Structure
|
||||
|
||||
1. Whether it fits the product boundary.
|
||||
2. Whether it follows Server, Agent, and frontend development rules.
|
||||
3. Whether it risks the publish, sync, rollback, or upgrade flow.
|
||||
4. Whether deployment, configuration, or README docs need updates.
|
||||
For details on the physical directory structure and responsibilities of each module (Server, Agent, Frontend, etc.), see [Repository Structure](./repository.md).
|
||||
|
||||
If a requirement expands the boundary or introduces new infrastructure, update design documentation first.
|
||||
## Environment Requirements
|
||||
|
||||
## Database Migrations
|
||||
| Item | Requirement |
|
||||
| --- | --- |
|
||||
| Go | `1.25+` |
|
||||
| Node.js | `18+` |
|
||||
| pnpm | Recommended enabling via `corepack enable` |
|
||||
| Docker | Required for Server containers, local integration testing, and Agent Docker images |
|
||||
| OpenResty | Required to execute `openresty` locally when running the Agent |
|
||||
| PostgreSQL | Optional; if not configured, the Server defaults to SQLite |
|
||||
|
||||
Any table, index, column type, sharding, or internal persistence metadata change must bump the database version and include an explicit migration from the previous version.
|
||||
## Initializing Frontend Dependencies
|
||||
|
||||
Migrations must validate the upgraded schema. Startup must stop if migration or validation fails.
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## Frontend Rules
|
||||
Build the static assets hosted by the Go Server:
|
||||
|
||||
`openflare_server/web` is the frontend baseline:
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
* Routes and layouts live in `app/`.
|
||||
* API calls are centralized under `lib/api/`.
|
||||
* Business logic belongs in `features/`.
|
||||
* Server state uses TanStack Query.
|
||||
* Forms use React Hook Form and Zod.
|
||||
* Theme supports `light`, `dark`, and `system`.
|
||||
## Starting the Server
|
||||
|
||||
SQLite Mode:
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export SESSION_SECRET='dev-session-secret'
|
||||
export SQLITE_PATH='./openflare-dev.db'
|
||||
export LOG_LEVEL='debug'
|
||||
go run .
|
||||
```
|
||||
|
||||
PostgreSQL Mode:
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
export SESSION_SECRET='dev-session-secret'
|
||||
export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
export LOG_LEVEL='debug'
|
||||
go run .
|
||||
```
|
||||
|
||||
Default access URL:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
The default credentials are `root` / `123456`.
|
||||
|
||||
## Starting the Frontend Dev Server
|
||||
|
||||
The frontend dev server listens to port `3001` by default and proxies requests to the backend via `NEXT_DEV_BACKEND_URL`:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
export NEXT_DEV_BACKEND_URL='http://127.0.0.1:3000'
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Access:
|
||||
|
||||
```text
|
||||
http://localhost:3001
|
||||
```
|
||||
|
||||
## Starting the Agent
|
||||
|
||||
Create a local `agent.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
export LOG_LEVEL='debug'
|
||||
go run ./cmd/agent -config ./agent.json
|
||||
```
|
||||
|
||||
If `openresty_path` is not configured, the Agent calls `openresty` by default. For debugging, you can explicitly configure `openresty_path`, `main_config_path`, `route_config_path`, `access_log_path`, `cert_dir`, `lua_dir`, and `runtime_config_dir`.
|
||||
|
||||
## Running Tests
|
||||
|
||||
Server:
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
Agent:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
Frontend:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
pnpm test:e2e
|
||||
```
|
||||
|
||||
Docs:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
Admin static assets:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Server binary:
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
go build -o openflare-server .
|
||||
```
|
||||
|
||||
Agent binary:
|
||||
|
||||
```bash
|
||||
cd openflare-agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
```
|
||||
|
||||
## Debugging Entrypoints
|
||||
|
||||
| Context | Command or Path |
|
||||
| --- | --- |
|
||||
| Server Logs | `LOG_LEVEL=debug go run .` |
|
||||
| Agent Logs | `LOG_LEVEL=debug go run ./cmd/agent -config ./agent.json` |
|
||||
| Swagger Docs | `http://localhost:3000/swagger/index.html` |
|
||||
| Frontend API Proxy | `NEXT_DEV_BACKEND_URL=http://127.0.0.1:3000 pnpm dev` |
|
||||
| OpenResty Validation | `openresty -t -c ./data/etc/nginx/nginx.conf` |
|
||||
|
||||
## Code Style & Change Admission
|
||||
|
||||
Before contributing, verify:
|
||||
|
||||
1. The requirement matches [Product Boundaries](./index.md).
|
||||
2. The implementation conforms to [Development Constraints](../guideline/development-constraints.md).
|
||||
3. The change does not disrupt publishing, sync, rollback, or upgrading lifecycles.
|
||||
4. Update corresponding documentation if configurations, deployments, APIs, or boundaries change.
|
||||
5. High-risk edits must be accompanied by unit tests or equivalent integration testing.
|
||||
|
||||
Database schema alterations must elevate the database version number and supply explicit migration and validation methods from the previous version.
|
||||
|
||||
+197
-14
@@ -1,21 +1,204 @@
|
||||
# Product Boundary
|
||||
# Product Boundaries
|
||||
|
||||
OpenFlare is a self-hosted OpenResty control plane for single-team or single-organization operations. It unifies reverse proxy configuration, node synchronization, certificate management, and basic observability.
|
||||
You will learn: What OpenFlare is, what problems it solves, who the target audience is, what current stable features are available, and which design boundaries cannot be bypassed during implementation.
|
||||
|
||||
Stable capabilities:
|
||||
OpenFlare is a self-hosted OpenResty control plane designed for single-team or single-organization internal operations. It solves the problems of decentralized management of reverse proxy configurations, node synchronization, certificate hosting, configuration publication and rollback, and basic observability.
|
||||
|
||||
## Project Positioning
|
||||
|
||||
OpenFlare is suitable for teams that need to centrally manage multiple OpenResty proxy nodes:
|
||||
|
||||
* Wanting to maintain reverse proxy website configurations using a management dashboard.
|
||||
* Wanting every configuration change to have a complete version history, preview, activation, and rollback support.
|
||||
* Wanting nodes to actively synchronize configurations, rather than the control plane SSHing into nodes to execute commands.
|
||||
* Wanting to manage TLS certificates, domain assets, node statuses, and basic access analytics in a single system.
|
||||
|
||||
OpenFlare is currently not positioned as a general-purpose logging platform, service mesh, Kubernetes Ingress Controller, or multi-tenant cloud platform.
|
||||
|
||||
## Current Capabilities
|
||||
|
||||
| Capability | Description |
|
||||
| --- | --- |
|
||||
| Reverse proxy management | Site-level configuration with multiple domains and origins |
|
||||
| Configuration versions | Preview, publish, activate, and rollback |
|
||||
| Agent sync | Registration, heartbeat, sync, and apply result reporting |
|
||||
| OpenResty management | Main template, performance options, cache options, and Lua assets |
|
||||
| HTTPS/TLS | Certificate storage and per-domain binding |
|
||||
| Basic observability | Request rollups, resource snapshots, health events, and access analytics |
|
||||
| Node management | Node state, tokens, deployment, and update flow |
|
||||
| Reverse Proxy Rules | Uses website configuration as the aggregation boundary, supporting multiple domains and origin settings. |
|
||||
| Website-level Config | One rule corresponds to one website, which can bind one or more domains and share site-level configurations. |
|
||||
| Origin Management | Maintains a lightweight origin directory and allows websites to save renderable origin snapshots. |
|
||||
| Config Versioning | Supports previews, publishing, activation, immutable history, and rollbacks. |
|
||||
| Agent Sync | Supports registration, heartbeats, synchronization, application result reporting, and self-updating. |
|
||||
| OpenResty Hosting | Manages main config templates, performance parameters, cache parameters, and Lua resources. |
|
||||
| HTTPS/TLS | Hosts certificate and domain assets, binding certificates on a per-domain basis. |
|
||||
| WAF | Maintains IP/CIDR block blacklists/whitelists, IP groups, and country-level geographic access controls at both global and site-specific levels. |
|
||||
| Basic Observability | Aggregates node requests, resource snapshots, health events, and access analytics. |
|
||||
| Node Management | Manages node status, token systems, and deployment/update lifecycles. |
|
||||
| Admin UI | Next.js-based official management dashboard. |
|
||||
| Auth Source Login | Supports configuring GitHub OAuth and standard OIDC login portals, allowing third-party accounts to bind to existing local users. |
|
||||
| Intranet Penetration | Securely exposes intranet HTTP services to the public internet using TunnelRelay nodes and the OpenFlared client, reusing the Agent's HTTPS/WAF capabilities. |
|
||||
|
||||
Default operating model:
|
||||
Default Working Model:
|
||||
|
||||
* All nodes consume the same globally active version.
|
||||
* Server stores configuration and state, but does not SSH into nodes.
|
||||
* Agent is the only controlled entry point on each node.
|
||||
* All nodes consume the same globally activated configuration version.
|
||||
* The Server stores configurations and state, and does not directly SSH to manage nodes.
|
||||
* The Agent is the only controlled entry point on the node side.
|
||||
* TunnelRelay nodes run both the Agent (OpenResty) and the Relay (frps manager) to provide intranet penetration relays.
|
||||
* The OpenFlared client runs inside the intranet, managing the frpc process to connect to the Relay and forward traffic to intranet services.
|
||||
|
||||
## Typical Use Cases
|
||||
|
||||
| Scenario | Description |
|
||||
| --- | --- |
|
||||
| Unified Entrance | Exposes multiple internal HTTP services via a unified domain and TLS certificate. |
|
||||
| Multi-Node Sync | Multiple OpenResty nodes consume the same active configuration version. |
|
||||
| Change Review | View previews or diffs before publishing, keeping an immutable history post-publish. |
|
||||
| Rapid Rollback | Re-activate an older version, letting the Agent pull and apply it. |
|
||||
| Certificate Hosting | Bind TLS certificates to different domains under the same website. |
|
||||
| Observability | Check node health status, aggregated requests, traffic analytics, and health events. |
|
||||
| Intranet Penetration | Exposes intranet HTTP services that are not directly reachable from the public internet using Tunnels, benefiting from HTTPS, WAF, and all other protections. |
|
||||
|
||||
## Website Configuration Constraints
|
||||
|
||||
`proxy_routes` is the aggregate object for "website configurations". One record corresponds to one website, which can bind one or more domains and share a set of site-level configurations.
|
||||
|
||||
Constraints:
|
||||
|
||||
* `proxy_routes.site_name` is the unique business identifier of the website.
|
||||
* `proxy_routes.domains` must contain at least one domain, and `domains[0]` is treated as the primary domain.
|
||||
* Any domain can globally belong to only one `proxy_routes`.
|
||||
* Site-level rate limits, reverse proxies, and caching configurations are shared by the site, with no per-domain differences allowed within the same website.
|
||||
* HTTPS allows binding certificates on a per-domain basis within the same site.
|
||||
|
||||
## Origin & Upstream Constraints
|
||||
|
||||
`origins` serve the reuse of the origin directory, storing only the origin address, display name, and remarks, without carrying protocols, ports, paths, weights, or health check policies. `proxy_routes` can optionally associate with an `origins` record, but the rule internally still saves a complete upstream snapshot for rendering.
|
||||
|
||||
Upstream Constraints:
|
||||
|
||||
* `proxy_routes` must contain at least one upstream address (for direct type `direct`), or be associated with a Tunnel (for intranet penetration type `tunnel`).
|
||||
* Multi-upstream load balancing is uniformly rendered into a named `upstream` with keepalive enabled.
|
||||
* A single upstream is allowed to carry a base path or query, which is appended in `proxy_pass`. Multi-upstream is strictly limited to pure `scheme://host[:port]` structures, and all upstreams in the same rule must use the same protocol.
|
||||
* `proxy_routes.origin_host` is an optional field used to override the `Host` header during back-to-source requests.
|
||||
* All direct upstream addresses must be valid `http://` or `https://` URLs.
|
||||
* Intranet penetration upstreams must associate with a valid `tunnel_id` and specify the intranet target address and protocol.
|
||||
|
||||
## Intranet Penetration Constraints
|
||||
|
||||
OpenFlare implements intranet penetration through TunnelRelay nodes and the OpenFlared client, built on top of frp (Fast Reverse Proxy).
|
||||
|
||||
### Node & Component Model
|
||||
|
||||
**Node Types**:
|
||||
|
||||
* `nodes.node_type` distinguishes the node type: `edge_node` (edge node, default) and `tunnel_relay` (tunnel relay).
|
||||
* TunnelRelay nodes run both the Agent (OpenResty) and the Relay (frps manager) concurrently, sharing the same `agent_token`.
|
||||
- The Agent is responsible for HTTPS termination, WAF protection, caching, and rate limiting.
|
||||
- The Relay manages the frps process, providing tunnel relay services for intranet clients.
|
||||
* TunnelRelay nodes introduce new fields: `node_type`, `relay_bind_port` (frpc connection port, default 7000), `relay_vhost_http_port` (HTTP Vhost port, default 8080), `relay_auth_token` (automatically generated), `relay_status`, etc.
|
||||
|
||||
**Tunnel Client**:
|
||||
|
||||
* The `tunnels` table independently stores intranet penetration client registration info and is decoupled from the `nodes` system.
|
||||
* Each Tunnel has a unique `tunnel_id` (format `tun-<32hex>`) and `tunnel_token` (client authentication credential).
|
||||
* The OpenFlared client runs inside the intranet, is not exposed to the public internet, uses `tunnel_token` for authentication, and communicates with the Server via `/api/flared/*` endpoints.
|
||||
* An OpenFlared client can connect to multiple Relays simultaneously for high availability.
|
||||
|
||||
### Upstream Type Expansion
|
||||
|
||||
The upstream configuration of `proxy_routes` is divided into two types, distinguished by the `upstream_type` field:
|
||||
|
||||
* **Direct Upstream (`direct`, default)**: Forwards traffic directly to the origin address, behaving exactly like the existing mechanism.
|
||||
* **Intranet Penetration Upstream (`tunnel`)**: Forwards traffic to the intranet service via a TunnelRelay node.
|
||||
- Must specify `tunnel_id` (associated with the `tunnels` table).
|
||||
- Must specify `tunnel_target_addr` (intranet target address, e.g., `192.168.1.100:8080`) and `tunnel_target_protocol` (`http` or `https`).
|
||||
- During publication, the Server automatically replaces the upstream address with `http://127.0.0.1:{relay_vhost_http_port}`.
|
||||
|
||||
### Traffic Paths & Protocols
|
||||
|
||||
**Complete Data Plane Traffic Path**:
|
||||
|
||||
```
|
||||
Browser → OpenResty (Agent, TLS/WAF) [TunnelRelay Node]
|
||||
↓
|
||||
frps (Relay, HTTP Vhost Routing) [TunnelRelay Node, 127.0.0.1:{vhost_port}]
|
||||
↓
|
||||
frp Tunnel Protocol (Host Header Routing)
|
||||
↓
|
||||
frpc (Client, Multi-process) [Intranet Server]
|
||||
↓
|
||||
Intranet Service (192.168.x.x:port)
|
||||
```
|
||||
|
||||
**Key Features**:
|
||||
|
||||
* frps uses the HTTP Vhost single-port reuse mechanism; all HTTP tunnels share one `vhost_port`, automatically routed to the corresponding frpc based on the Host header.
|
||||
* The Agent preserves the original `Host` header, which frps uses to match the virtual host.
|
||||
* Each tunnel corresponds to a single `proxy_routes` and can bind multiple domains.
|
||||
* The OpenFlared client manages an independent frpc process for each connected Relay, transmitting multiple HTTP proxy definitions via a single frp tunnel.
|
||||
|
||||
### Configuration Sync Model
|
||||
|
||||
The publication process generates two types of configuration version data simultaneously, linked by a single `config_version` version number:
|
||||
|
||||
* **Agent-side Config**: OpenResty main configuration + route configurations + WAF rules. If a tunnel upstream is included, it is automatically rendered as a `http://127.0.0.1:{vhost_port}` upstream.
|
||||
* **Tunnel-side Config**: Relay list + frpc proxy definitions. Versioned alongside the publishing process; changes are hot-reloaded using `frpc reload` first.
|
||||
* **Relay Config**: Dispatched via heartbeat responses, relatively static, and not included in the versioned publishing flow.
|
||||
|
||||
### Tunnel Design Constraints
|
||||
|
||||
* Only HTTP protocol tunnel traffic is supported (keeping TCP/UDP tunnels extensible); separate TCP/UDP port allocation is not supported for now.
|
||||
* The DNS for domains using Tunnel upstreams should resolve to the designated TunnelRelay node.
|
||||
* frp binaries (v0.61+) are packaged and provided by the system deployment script or container images.
|
||||
|
||||
## HTTPS Constraints
|
||||
|
||||
`proxy_routes.domain_cert_ids` is used to record the domain-certificate bindings parallel to `domains`; a value of `0` means the domain does not have HTTPS enabled and stays HTTP-only.
|
||||
|
||||
During rendering:
|
||||
|
||||
* Domains with certificates are grouped by certificate and output as independent `443 ssl` `server` blocks.
|
||||
* Domains without certificates bound must not be automatically routed to HTTPS.
|
||||
* All domains in `proxy_routes.domains` must be kept in the same site configuration to avoid being split across version snapshots.
|
||||
|
||||
## WAF Constraints
|
||||
|
||||
WAF centers around rule groups. The system provides a single global rule group (applied to all sites by default), on top of which websites can overlay multiple custom rule groups.
|
||||
|
||||
Core Capabilities:
|
||||
|
||||
* Supports individual IP / CIDR block whitelists and blacklists.
|
||||
* Supports IP group references (including manual, automatic Expr calculated, and URL subscribed IP groups).
|
||||
* Supports GeoIP-based country/region level admission filtering.
|
||||
* Supports custom interception responses for rule groups (custom status codes and interception HTML pages, default is `418`).
|
||||
|
||||
IP Group & Judgment Constraints:
|
||||
|
||||
* **Runtime Decoupling**: The WAF runtime only reads local JSON files and does not access the Server database; configuration versions only store referenced IP group IDs. IP group members are synchronized via MD5 checksum differences and WebSocket push notifications, achieving hot activation without reloading Nginx.
|
||||
* **Built-in Expr Rules**:
|
||||
* High-frequency 404 scanning block: `request_count > 100 && status_404_ratio >= 0.8`
|
||||
* Malicious IP direct probe: `ip_host_count > 50 && ip_host_ratio > 0.5`
|
||||
* **Decision Priority**: The whitelist has absolute priority. If it does not match the whitelist, the blacklist funnel is triggered (global rule group first, custom groups matched in ascending ID order).
|
||||
* GeoIP resolution depends on the local MaxMind database; if GeoIP is anomalous, region rules are automatically ignored and must not disrupt the availability of IP rules and the main reverse proxy chain.
|
||||
|
||||
## Authentication Source Constraints
|
||||
|
||||
`auth_sources` uniformly supports `github` and `oidc` login configurations. `external_accounts` stores bindings between third-party accounts and local users. Logic for first-time third-party login:
|
||||
|
||||
* If already bound, directly authorize login; if there is an active local session, automatically bind.
|
||||
* If unbound and registration is enabled, automatically create a local account; if registration is closed, require the user to provide an existing local username and password to establish the association.
|
||||
|
||||
## Version & Observability Constraints
|
||||
|
||||
* `config_versions` must save the complete snapshot, rendering result, and `checksum`.
|
||||
* Globally, only one version can be active at a time.
|
||||
* Rollback is achieved by re-activating an older version.
|
||||
* `nodes` only carry control plane state and low-frequency summaries; they do not carry high-frequency observability facts.
|
||||
* Metrics, trends, and access analytics prioritize server-side aggregation rather than client-side temporary statistics.
|
||||
* Access detail logs are only retained within a controlled time window, not evolving into a general logging platform.
|
||||
|
||||
## Documentation Maintenance Principles
|
||||
|
||||
* Update this document when the product range or system boundaries change.
|
||||
* Update [System Architecture](./architecture.md) when the system structure or module responsibilities change.
|
||||
* Update [Agent & Publish Model](./agent-design.md) when the publishing, synchronization, rollback, or Agent model changes.
|
||||
* Update [Development Constraints](../../guideline/Constraints.md) when developer constraints, code specifications, or API conventions change.
|
||||
* Update README and [Deployment Instructions](../../deployment/deployment.md) when deployment methods change.
|
||||
* Update [Configurations Reference](../reference/configuration.md) when configuration items change.
|
||||
* Completed phases should no longer be backfilled as "version plans".
|
||||
* Before starting a new phase, complement the design first, then proceed to implementation.
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# Release Model
|
||||
|
||||
OpenFlare publishes complete configuration versions instead of modifying node configuration online.
|
||||
|
||||
```text
|
||||
Edit rules -> Preview / diff -> Publish -> Create full version -> Activate -> Agent pulls -> Agent applies -> Agent reports
|
||||
```
|
||||
|
||||
## Publish Rules
|
||||
|
||||
Server must:
|
||||
|
||||
1. Read all enabled `proxy_routes`.
|
||||
2. Read the OpenResty main template and structured options.
|
||||
3. Render the full OpenResty configuration.
|
||||
4. Compute `checksum`.
|
||||
5. Write `config_versions`.
|
||||
6. Switch the active version.
|
||||
7. Let Agents discover and apply it in later heartbeats.
|
||||
|
||||
Version numbers use `YYYYMMDD-NNN`.
|
||||
|
||||
## Immutable History
|
||||
|
||||
Historical versions are immutable. Rollback reactivates an old version.
|
||||
|
||||
Only one global active version exists at a time. Node-specific version groups are not part of the current model.
|
||||
|
||||
## Agent Apply Strategy
|
||||
|
||||
Agent backs up old files, writes the new main config, route config, certificates, and Lua assets, then validates and reloads.
|
||||
|
||||
If activation fails, Agent attempts to recover. A failed `version + checksum` is blocked locally until the remote active version or checksum changes.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Repository Structure
|
||||
|
||||
You will learn: The responsibilities of Server, Agent, Frontend, scripts, and documentation folders in the OpenFlare repository, and where to place logic when contributing code.
|
||||
|
||||
| Path | Responsibility |
|
||||
| --- | --- |
|
||||
| `openflare-server` | Gin + GORM + SQLite/PostgreSQL single monolithic control plane |
|
||||
| `openflare-server/web` | Next.js 15 App Router Admin Frontend, hosted by Go Server |
|
||||
| `openflare-agent` | Go monolithic Agent running on the node side |
|
||||
| `openflare-relay` | Tunnel relay daemon running on public edges, managing frps processes |
|
||||
| `openflared` | Tunnel client running on intranet servers, managing frpc processes |
|
||||
| `scripts` | System helper scripts for installation, self-updating, etc. |
|
||||
| `docs` | VitePress documentation website, design baselines, specifications, and configurations |
|
||||
| `docs/en` | English version of documentation |
|
||||
|
||||
## Server Layering
|
||||
|
||||
| Folder | Responsibility |
|
||||
| --- | --- |
|
||||
| `controller/` | Parameter parsing, service calling, and returning responses |
|
||||
| `service/` | Business logic, validations, transaction orchestration, and configuration rendering |
|
||||
| `model/` | Model definitions, database versioning, and migrations |
|
||||
| `router/` | Route registration |
|
||||
| `middleware/` | Cross-cutting concerns like authentication, authorization, rate limiting, CORS, and Turnstile |
|
||||
| `common/` | Configurations, global states, and initialization entrypoints |
|
||||
| `utils/` | Pure utility functions and general helpers |
|
||||
| `job/` | Periodic cron tasks (such as SSL certificate auto-renewals) |
|
||||
| `upload/` | File upload handlers |
|
||||
| `docs/` | API documentation (Swagger) |
|
||||
| `data/` | Static data (such as GeoIP databases) |
|
||||
|
||||
## Agent Modules
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
| `config/` | Configuration loading and default values |
|
||||
| `heartbeat/` | Heartbeat check-in and configuration version evaluation |
|
||||
| `sync/` | Configuration fetching and application orchestration |
|
||||
| `nginx/` | OpenResty file writing, validation, reloads, startup, and rollbacks |
|
||||
| `state/` | Local states and buffers for metric reporting |
|
||||
| `httpclient/` | Server HTTP API communication |
|
||||
| `wsclient/` | WebSocket client communication |
|
||||
| `protocol/` | Agent API protocol types and structures |
|
||||
| `updater/` | Agent self-updating logic |
|
||||
| `logging/` | Logging processing |
|
||||
| `observability/` | Observability (metrics, tracing, etc.) |
|
||||
| `geoipdata/` | GeoIP database handling |
|
||||
| `geoipupdate/` | GeoIP database updates |
|
||||
| `agent/` | Core Agent bootstrap and lifecycle orchestration |
|
||||
|
||||
## Frontend Layering
|
||||
|
||||
| Folder | Responsibility |
|
||||
| --- | --- |
|
||||
| `app/` | Next.js App Router routes, layouts, and page assemblies |
|
||||
| `features/` | Feature modules organized by business domains |
|
||||
| `components/` | Reusable UI components shared across features |
|
||||
| `lib/` | API clients, environment configurations, utility functions, and constants |
|
||||
| `store/` | Lightweight cross-page UI state management |
|
||||
| `types/` | Shared TypeScript type definitions |
|
||||
| `styles/` | Global stylesheets |
|
||||
| `tests/` | Frontend unit and integration tests (Vitest, Playwright) |
|
||||
| `scripts/` | Build and deployment scripts |
|
||||
| `public/` | Static assets |
|
||||
|
||||
## Relay Modules
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
| `cmd/` | CLI startup entrypoint and main bootstrap functions |
|
||||
| `internal/config/` | Local configurations parsing and defaults initialization |
|
||||
| `internal/frps/` | Manages the lifecycle of the frps process, monitoring its status |
|
||||
| `internal/heartbeat/` | Periodic HTTP heartbeat, status reporting, and update retrievals |
|
||||
| `internal/httpclient/` | General API client for calling the Server |
|
||||
| `internal/observability/` | Host and frps metrics collection and pre-aggregation |
|
||||
| `internal/relay/` | Coordinates the core Relay lifecycle, setup, and cleanup |
|
||||
| `internal/state/` | Local runtime states, error logs, and persistent caches |
|
||||
| `internal/updater/` | Relay update check, download installation, and restarts |
|
||||
| `internal/wsclient/` | Bi-directional real-time WebSocket connection to the Server |
|
||||
|
||||
## OpenFlared (Client) Modules
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
| `cmd/` | CLI startup entrypoint and main bootstrap functions |
|
||||
| `internal/config/` | Local client configurations loading and parsing |
|
||||
| `internal/flared/` | Core client scheduling and tunnel lifecycle orchestration |
|
||||
| `internal/frpc/` | Dynamically generates `frpc.toml` configs for multiple Relays and monitors frpc processes |
|
||||
| `internal/heartbeat/` | Heartbeat communications with control planes, including token checks |
|
||||
| `internal/httpclient/` | General API client for Server communication |
|
||||
| `internal/sync/` | Incrementally pulls latest Tunnel route bindings, generates snapshots, and applies them |
|
||||
| `internal/updater/` | Client self-update, new version check, and upgrade installation |
|
||||
| `internal/wsclient/` | Bi-directional WebSocket client for real-time tunnel configuration pushes |
|
||||
@@ -0,0 +1,130 @@
|
||||
# Intranet Penetration Tunnel Design Document
|
||||
|
||||
You will learn: The architectural design of the OpenFlare intranet penetration tunnel, the internal principles of the dual-ended control components (Relay and Client), their interaction logics, and the communication flows for the data plane and control plane.
|
||||
|
||||
---
|
||||
|
||||
## Requirements Analysis
|
||||
|
||||
In typical web application hosting scenarios, many origin servers (Origin Servers) are deployed in local intranet environments (such as local development machines, LAN servers, or firewalled private clusters). These servers typically suffer from:
|
||||
1. **No Public IP**: Cannot be directly accessed by public internet traffic.
|
||||
2. **Security Compliance Restrictions**: Creating port mappings (NAT) on border routers is strictly prohibited by security policies.
|
||||
3. **Dynamic IP Changes**: Traditional DDNS solutions exhibit high latency and are highly unstable.
|
||||
|
||||
To allow internal origin servers to seamlessly integrate into the OpenFlare global data gateway, benefiting from premium features like WAF geographic protection and TLS certificate hosting, OpenFlare designed an end-to-end solution based on a **reverse relay penetration tunnel**. In this architecture, public edge nodes act as reverse proxy entrances and traffic relays, while the intranet side only needs to initiate secure outbound connections to achieve secure and stable reverse penetration of public traffic to internal origin servers.
|
||||
|
||||
---
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
The intranet penetration tunnel subsystem includes the following core capabilities:
|
||||
|
||||
* **Dynamic Relay Node Management**: The control plane dynamically dispatches relay services (frps), distributing service ports and authentication tokens dynamically.
|
||||
* **Multi-Tunnel Reverse Proxy Mapping**: Supports mapping multiple internal web ports on a single intranet client, binding multiple domain routes to corresponding relay nodes.
|
||||
* **Independent Process Lifecycle Control**: Both the relay and client are independent daemon processes written in Go, responsible for spawning, monitoring, self-healing, and hot-upgrading the underlying frp engine.
|
||||
* **Token-based Independent Authentication**: The relay uses `agent_token` for authorization, whereas the intranet client uses its dedicated `tunnel_token`, enforcing isolation of permissions and routing boundaries.
|
||||
* **Validation & Incremental Hot Reload**: Config files are rewritten and processes are gracefully reloaded only when tunnel bindings, certificates, or Relay topologies change, reducing runtime overhead.
|
||||
|
||||
---
|
||||
|
||||
## Intranet Penetration & Tunnel Architecture
|
||||
|
||||
The intranet penetration subsystem is integrated on top of the mature and high-performance `frp` tunnel protocol, divided into the **Control Plane** and the **Data Plane**.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
%% Data Flow
|
||||
Browser[1. Browser / Visitor] -->|HTTPS Request| Agent[2. OpenResty / Agent]
|
||||
Agent -->|Local proxy_pass| RelayFrps[3. OpenFlare Relay / frps]
|
||||
RelayFrps -->|Encrypted Tunnel Protocol| FlaredFrpc[4. OpenFlared / frpc]
|
||||
FlaredFrpc -->|Forward Local Request| LocalOrigin[5. Intranet Origin 192.168.x.x]
|
||||
|
||||
%% Control Flow & Heartbeats
|
||||
Server[OpenFlare Server Control Plane] <-->|Relay API / Heartbeat| RelayManager[openflare-relay process]
|
||||
Server <-->|Client API / Heartbeat| ClientManager[openflared process]
|
||||
|
||||
RelayManager -.->|Control Process & Config| RelayFrps
|
||||
ClientManager -.->|Control Multi-Relay Processes| FlaredFrpc
|
||||
|
||||
style Browser fill:#f9f,stroke:#333,stroke-width:2px
|
||||
style LocalOrigin fill:#9f9,stroke:#333,stroke-width:2px
|
||||
style Server fill:#f96,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
* **Control Plane**: The Server maintains the database state. The `openflare-relay` process on relay nodes and the `openflared` process on intranet servers synchronize tunnel configurations via HTTP heartbeats and long-lived WebSocket connections.
|
||||
* **Data Plane**: Public traffic enters the public edge Agent (OpenResty), where the TLS handshake, HTTPS termination, and WAF filtering are executed. It is then forwarded via `proxy_pass` to the co-located `openflare-relay (frps)` on the loopback address. `frps` encapsulates the HTTP requests into the encrypted TCP tunnel and sends them down to the intranet `openflared (frpc)`. Finally, `frpc` unpacks the requests and forwards them to the actual intranet origin service.
|
||||
|
||||
---
|
||||
|
||||
## Relay (Server-side) Design
|
||||
|
||||
`openflare-relay` is a relay manager deployed on the public edge, running on nodes of type `tunnel_relay`.
|
||||
|
||||
### 1. Core Architecture & Logic
|
||||
* **Process Daemon**: The Relay process embeds the `frps` binary, spawning the `frps -c frps.toml` subprocess via `exec.Command` and using goroutines to asynchronously listen to its exit status. If `frps` exits unexpectedly, it automatically restarts using an exponential backoff policy.
|
||||
* **Dynamic Configuration Rendering**: Periodically synchronizes status with the control plane via HTTP heartbeats to retrieve the active `RelayConfig`, including:
|
||||
* `bindPort`: The public control port that frps listens to for incoming intranet frpc connections.
|
||||
* `vhostHTTPPort`: The virtual host HTTP listening port where the Agent's proxy_pass points.
|
||||
* `authToken`: The security credential used during the client connection handshake.
|
||||
* `webServer`: Enables the frps dashboard API, which the Relay queries to collect active tunnel counts and traffic metrics.
|
||||
* **Status Reporting**: In each heartbeat cycle, the Relay reports the active connections, registered clients, individual proxy tunnel statuses, and Relay version back to the Server.
|
||||
|
||||
---
|
||||
|
||||
## Openflared (Client-side) Design
|
||||
|
||||
`openflared` is the client manager running inside the user's intranet server, authenticated using a dedicated `tunnel_token`.
|
||||
|
||||
### 1. Core Design Mechanisms
|
||||
* **Multi-Relay Support (Multiplexing)**:
|
||||
To guarantee high availability and geographical proximity, the control plane may schedule the client to connect to multiple public Relays. `openflared` parses the list of Relays dispatched in the `TunnelConfig`, generating dedicated configurations (`frpc_<relay_node_id>.toml`) and allocating distinct cancelable contexts for each Relay process locally.
|
||||
* **Independent Subprocess Monitoring**:
|
||||
`openflared` maintains a local `processes` map to manage the lifecycles of individual `frpc` subprocesses. When the control plane adds or removes Relays, the client incrementally spawns new processes or gracefully shuts down obsolete ones without affecting other functioning tunnels.
|
||||
* **Dynamic TOML Generation**:
|
||||
When rendering TOML configs for each Relay, the client iterates over the Proxies list, writing each intranet service's `LocalAddr`, `LocalPort`, and bound `CustomDomains` into standard `[[proxies]]` blocks.
|
||||
|
||||
---
|
||||
|
||||
## Interaction Logic & Traffic Model
|
||||
|
||||
The intranet penetration subsystem implements consistent version control and status feedback loops.
|
||||
|
||||
### 1. Control Plane Publishing & Sync Flow
|
||||
|
||||
```text
|
||||
Admin modifies tunnel/intranet port mappings -> Click Publish -> Generate new Tunnel version & Checksum
|
||||
|
|
||||
v (Push or Heartbeat Pull)
|
||||
+-----------------------------------------------------------------------+-----------------------------------------------------------------------+
|
||||
| |
|
||||
v (Relay Side) v (Client Side)
|
||||
openflare-relay heartbeat detects frps port/Token change openflared heartbeat detects tunnel_version change
|
||||
Re-render local frps.toml Request full proxy configuration details
|
||||
Kill and restart the frps process Re-render frpc_<relay_id>.toml configs
|
||||
Report health status as healthy Restart or hot-reload changed frpc processes
|
||||
Report application results (Apply Success/Error)
|
||||
```
|
||||
|
||||
1. **Versioned Controls**: All intranet tunnel routes and mapping relationships are version-controlled, dispatching a unique `version` and `checksum` to ensure clients do not repeatedly write files or trigger redundant reloads.
|
||||
2. **Closed-Loop Application Feedback**: After applying new configurations, the client reports the application result in the next heartbeat. If the intranet port is unreachable or certificate bindings fail, the client intercepts the stdout/stderr of the subprocess to report `LastError` to the Server, providing administrators with transparent error details.
|
||||
|
||||
### 2. Data Plane Traffic Model
|
||||
1. **Public Entrance (Agent)**:
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name intranet.example.com;
|
||||
# ... TLS certificates & WAF filtering ...
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:18080; # Points to local frps vhost port
|
||||
proxy_set_header Host $host; # Must preserve the original Host header, which frps relies on to route requests
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
2. **Relay Node (frps)**:
|
||||
`frps` listens to the Vhost port `18080`. When an HTTP request arrives, it extracts `Host: intranet.example.com` from the request headers and searches its active registered tunnel registry to locate the matching encrypted TCP connection (initiated by the intranet frpc).
|
||||
3. **Encrypted Tunnel Transmission (TCP)**:
|
||||
`frps` encapsulates the HTTP request into the custom TCP tunnel protocol and transmits it down to the intranet `frpc` client.
|
||||
4. **Intranet Client Distribution (frpc)**:
|
||||
The `frpc` instance managed by `openflared` receives the payload, resolves it according to local settings (`localIP = "127.0.0.1"`, `localPort = 8080`), initiates a local TCP connection to forward the request to the intranet web service, and returns the response back through the tunnel to the public viewer.
|
||||
@@ -0,0 +1,132 @@
|
||||
# WAF Design Document
|
||||
|
||||
You will learn: The core architecture of the OpenFlare edge Web Application Firewall (WAF), the dynamic IP group asynchronous differential sync model, the high-performance OpenResty Lua caching scheme, and the complete request filtering and decision logic.
|
||||
|
||||
---
|
||||
|
||||
## Requirements Analysis
|
||||
|
||||
In public internet environments, web applications face a wide variety of security threats (such as scanner profiling, api scraping, malicious botnets targeted at specific regions, ransomware, and CC attacks). Allowing malicious requests to pass directly to the origin server (Origin Server) results in:
|
||||
1. **Origin Server Overload**: High-frequency database queries and intensive CPU computations easily exhaust server resources.
|
||||
2. **Sensitive API Abuse**: APIs like login, registration, and SMS verification codes can be maliciously exploited, leading to financial and computational losses.
|
||||
3. **Data Exposure Risks**: Malicious common vulnerability probing actions are not intercepted proactively.
|
||||
|
||||
Therefore, OpenFlare needs to build a **high-performance, resiliently scalable WAF filtering engine** at the frontmost data plane layer (OpenResty). This engine is capable of executing deep filtering on malicious requests at the edge layer closest to users with sub-millisecond overhead. This relieves pressure on origin servers and provides core security capabilities like CC protection (PoW challenge), IP whitelisting/blacklisting, and region-level interception.
|
||||
|
||||
---
|
||||
|
||||
## Core Capabilities
|
||||
|
||||
OpenFlare WAF includes the following core protection dimensions:
|
||||
|
||||
* **IP Interception (IP Whitelist/Blacklist)**: Supports filtering by single IP or CIDR block, and aggregating tens of thousands of IPs into IP groups for highly efficient matching.
|
||||
* **Geographical Whitelist/Blacklist (GeoIP Limit)**: Integrates MaxMind databases to support precise admission controls based on countries and provinces/regions.
|
||||
* **Custom Interception Responses**: Supports custom block status codes (e.g., 403, 418) and personalized HTML block pages for different filtering rules.
|
||||
* **Human-Machine Challenge (PoW CC Protection)**: Supports seamless client-side PoW challenges, calculating Hash collisions to prevent automated scripts and botnets from hitting endpoints concurrently.
|
||||
|
||||
---
|
||||
|
||||
## IP Group Design & Dynamic Asynchronous Sync
|
||||
|
||||
IP groups are the core containers for highly efficient IP whitelisting and blacklisting. OpenFlare classifies IP groups into three types based on their update frequencies and source channels:
|
||||
|
||||
### 1. IP Group Types
|
||||
* **Manual**: Manually input by administrators in the control panel. Primarily used for static trusted IPs or long-term blocks.
|
||||
* **Subscription**: Configured with remote text feeds (one IP/CIDR per line) or standard JSON subscription URLs. Server-side cron jobs periodically fetch and parse the remote subscription sources. Primarily used for integrating open-source threat intelligence feeds, cloud provider IP ranges, etc.
|
||||
* **Automatic**: **The most resilient dynamic protection channel**. Control plane scanning jobs read access logs from all nodes, performing aggregation and analysis based on configured Expr rules (e.g., "requesting the `/api/login` endpoint over 50 times with a 401 status code in 5 minutes"). Once matched, the source IP is automatically added to a temporary block list for a specified duration.
|
||||
|
||||
### 2. Asynchronous Differential Sync Design (No Nginx Reload)
|
||||
In traditional Nginx WAF designs, IP blacklist updates typically require writing configurations and executing reloads. If malicious IP blocks occur at high frequencies (seconds or minutes), frequent reloads force Nginx to constantly spawn new worker processes and tear down old ones, severely degrading performance.
|
||||
|
||||
OpenFlare adopts a **dynamic IP group asynchronous differential sync design**:
|
||||
|
||||
```text
|
||||
WAF IP member updates (Manual/Subscription/Auto-trigger)
|
||||
|
|
||||
v
|
||||
Server updates the database and calculates the new MD5 Checksum of the IP group
|
||||
|
|
||||
+----------------------------------------+
|
||||
| (WebSocket Real-time Broadcast) | (Heartbeat Fallback Comparison)
|
||||
v v
|
||||
Server immediately pushes complete members Agent heartbeats report the local IP groups
|
||||
of modified groups to all Agents checksum mapping table
|
||||
| |
|
||||
| v
|
||||
| Server detects Checksum mismatch and dispatches
|
||||
v the modified IP group members
|
||||
Agent receives member data and writes it as JSON to local disk: waf_ip_groups.json
|
||||
|
|
||||
v (Lua Memory Awareness)
|
||||
OpenResty Lua engine detects file changes via MD5 checksum in seconds and hot-updates its memory,
|
||||
completely bypassing Nginx process reloads.
|
||||
```
|
||||
|
||||
Through this architecture, the persistence and activation of tens of thousands of highly volatile dynamic blacklist IPs **require absolutely no Nginx reloads**, maximally protecting the high-concurrency throughput of the gateway.
|
||||
|
||||
---
|
||||
|
||||
## Rule Groups & Site Bindings
|
||||
|
||||
* **WAF Rule Group**: The smallest logical collection of WAF filtering policies. A single rule group can contain IP whitelists/blacklists, IP group references, regional restrictions, and CC protection configurations.
|
||||
* **Global Rule Group**: When a rule group is marked as `is_global = true`, it takes effect on **all website routes** hosted on the node by default.
|
||||
* **Site Binding**: Website routes (`proxy_routes`) can bind one or more non-global rule groups. During request validation, WAF evaluates the union of `Global Rule Group + Bound Rule Groups`.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Details & High-Performance Caching
|
||||
|
||||
WAF is triggered in the OpenResty `access_by_lua` phase, implemented primarily through Lua files and local JSON configurations.
|
||||
|
||||
### 1. Physical Structures
|
||||
* `waf_config.json`: Contains metadata for all rule groups, geographic country/region limits, and website-to-rule-group bindings.
|
||||
* `waf_ip_groups.json`: Contains all synchronized IP groups and their corresponding IP lists.
|
||||
* `waf/runtime.lua`: The actual runtime engine responsible for WAF rule comparison.
|
||||
* `waf/check.lua`: The entry point for the access layer, handling packages inclusion and triggering `check()`.
|
||||
|
||||
### 2. Shared Memory Dictionary (ngx.shared) High-Performance Cache Design
|
||||
Reading JSON files from the disk and decoding them upon every incoming web request would make disk I/O a severe performance bottleneck.
|
||||
|
||||
OpenFlare leverages the **OpenResty Shared Memory Dictionary (ngx.shared.openflare_waf_config)** to implement a two-level caching mechanism:
|
||||
|
||||
1. **Zero File I/O Path**:
|
||||
In Lua, every time `check()` executes, it first computes the MD5 hash of the local JSON file using `ngx.md5` (which takes virtually zero time since the file is cached in the OS Page Cache).
|
||||
2. **Hash Comparison & Hot Loading**:
|
||||
It compares this against the cached hash key (`_config_hash`) stored in the shared memory dictionary.
|
||||
* **If the hash is unchanged**: It reads the pre-decoded Lua Table configuration stored directly in shared memory. The entire verification runs purely in **shared memory**, completing in **microseconds**.
|
||||
* **If the hash is mismatched**: Indicating that the Agent has just updated the WAF rules or IP groups on the disk, the Lua engine automatically reads the disk file, decodes it via `cjson.decode`, writes the decoded data and the new MD5 hash into shared memory, and makes it seamlessly readable by all subsequent worker processes.
|
||||
|
||||
---
|
||||
|
||||
## Application Flow & Decision Judgment Control Logic
|
||||
|
||||
When an HTTP/HTTPS request arrives at OpenResty, WAF evaluates and intercepts it step-by-step in the `access` phase according to the funnel decision chain below:
|
||||
|
||||
### 1. WAF Decision Flowchart
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Request enters access phase] --> B[Get Site Name of current request]
|
||||
B --> C[Load all active rule groups bound to this Site in shared memory]
|
||||
C --> D{Matches IP whitelist or Whitelist IP group?}
|
||||
D -- Yes (Matched) --> E[Pass request - ALLOW]
|
||||
D -- No --> F{Matches country/region whitelist?}
|
||||
F -- Yes (Matched) --> E
|
||||
F -- No --> G{Matches IP blacklist or Blacklist IP group?}
|
||||
G -- Yes (Matched) --> H[Block request - BLOCK]
|
||||
G -- No --> I{Matches country/region blacklist?}
|
||||
I -- Yes (Matched) --> H
|
||||
I -- No --> J{Is CC PoW verification enabled?}
|
||||
J -- Yes --> K[Transfer to CC Protection module]
|
||||
J -- No --> L[No security risks, pass normally]
|
||||
|
||||
H --> M[Exit and return custom status code and block page HTML configured in the rule group]
|
||||
```
|
||||
|
||||
### 2. Decision Step Details
|
||||
1. **Whitelist Precedence**:
|
||||
To prevent false positives and guarantee smooth passage of core back-to-source traffic (such as search engine spiders, CDN back-to-source IPs, and office egresses), WAF **prioritizes matching IP whitelists and regional whitelists**. Once a whitelist matches, it immediately bypasses all subsequent blacklist checks and CC challenges.
|
||||
2. **Blacklist Aggressive Block**:
|
||||
If a request is not captured by the whitelist evaluation, it enters the blacklist funnel. Once the source IP matches an IP blacklist, a referenced blacklist IP group, or lies within a prohibited country/region, the Lua engine immediately marks `ngx.ctx.openflare_waf_blocked` as `true`.
|
||||
3. **Response Output**:
|
||||
Upon hitting the blacklist, Lua extracts the `block_status_code` (defaults to 418 or 403) and `block_response_body` (interception HTML page) configured in the matching rule group. It outputs the response body via `ngx.say()` and gracefully terminates the request using `ngx.exit(status)` to prevent the request from passing upstream.
|
||||
@@ -1,60 +0,0 @@
|
||||
# Connect Agent
|
||||
|
||||
OpenFlare Agent runs on proxy nodes. It handles registration, heartbeat, configuration sync, OpenResty file writes, validation, reload, rollback, and self-update.
|
||||
|
||||
## Authentication
|
||||
|
||||
| Method | Use case |
|
||||
| --- | --- |
|
||||
| `agent_token` | The node already exists or has a dedicated credential |
|
||||
| `discovery_token` | First-time auto-registration; Server exchanges it for a node token |
|
||||
|
||||
At least one of them is required.
|
||||
|
||||
## Install Script
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Or with discovery:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Configuration Example
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_container_name": "openflare-openresty",
|
||||
"openresty_docker_image": "openresty/openresty:alpine",
|
||||
"openresty_observability_port": 18081,
|
||||
"observability_replay_minutes": 15,
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
Without `openresty_path`, Agent uses Docker OpenResty by default.
|
||||
|
||||
## Run from Source
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
export LOG_LEVEL='info'
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
@@ -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).
|
||||
@@ -1,102 +0,0 @@
|
||||
# Deployment
|
||||
|
||||
This page summarizes the OpenFlare deployment baseline, integration flow, upgrade entry points, and Agent install scripts.
|
||||
|
||||
## Requirements
|
||||
|
||||
Server:
|
||||
|
||||
* Go 1.25+
|
||||
* Node.js 18+
|
||||
* Writable SQLite directory or reachable PostgreSQL instance
|
||||
|
||||
Agent:
|
||||
|
||||
* Go 1.25+
|
||||
* Writable Agent data directory
|
||||
* Local mode requires `openresty -t` and `openresty -s reload`
|
||||
* Docker mode requires Docker access
|
||||
|
||||
## Docker Compose
|
||||
|
||||
PostgreSQL is recommended for production:
|
||||
|
||||
```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
|
||||
container_name: openflare
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
SESSION_SECRET: replace-with-random-string
|
||||
SQLITE_PATH: /data/openflare.db
|
||||
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:
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Open `http://localhost:3000`. The default account is `root` / `123456`; change the password immediately.
|
||||
|
||||
## Agent Install
|
||||
|
||||
Using `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 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
|
||||
```
|
||||
|
||||
Supported options include `--server-url`, `--discovery-token`, `--agent-token`, `--install-dir`, `--repo`, and `--no-service`.
|
||||
|
||||
## Validation
|
||||
|
||||
1. Prepare `agent_token` or `discovery_token` in the console.
|
||||
2. Start Agent and confirm the node is online.
|
||||
3. Add an enabled reverse proxy site.
|
||||
4. Publish and activate a new version.
|
||||
5. Confirm Agent pulls, validates, reloads, and reports the result.
|
||||
|
||||
## Uninstall Agent
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
+84
-16
@@ -1,32 +1,100 @@
|
||||
# Publish First Site
|
||||
# Publishing Your First Site
|
||||
|
||||
OpenFlare publishes complete configuration versions. After editing a site, publish and activate a new version before Agents apply it.
|
||||
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.
|
||||
|
||||
## Create Site Configuration
|
||||
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.
|
||||
|
||||
Required fields:
|
||||
## 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 |
|
||||
| --- | --- |
|
||||
| Site name | Business-unique identifier; defaults to the primary domain when omitted |
|
||||
| Domains | At least one domain; the first one is the primary domain |
|
||||
| Origin URL | Valid `http://` or `https://` upstream URL |
|
||||
| Enabled | Only enabled sites are rendered into releases |
|
||||
| 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 |
|
||||
|
||||
A domain can belong to only one site.
|
||||
Example:
|
||||
|
||||
## Bind Certificates
|
||||
| Field | Example |
|
||||
| --- | --- |
|
||||
| Website Name | `app` |
|
||||
| Domain | `app.example.com` |
|
||||
| Origin Address | `http://10.0.0.20:8080` |
|
||||
|
||||
HTTPS certificates are bound per domain. Domains without certificates are not automatically rendered into `443 ssl` server blocks.
|
||||
A single domain can belong to only one website configuration. Rate limiting, reverse proxy, and caching parameters are shared site-wide.
|
||||
|
||||
## Publish and Activate
|
||||
## 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
|
||||
Edit rules -> Preview / diff -> Publish -> Create full version -> Activate -> Agent pulls -> Agent applies -> Agent reports
|
||||
Modify rules -> Preview / Diff -> Publish -> Generate complete version -> Activate version -> Agent pulls -> Local application -> Report result
|
||||
```
|
||||
|
||||
Server reads enabled sites, OpenResty template, performance options, and cache options, renders a full configuration, computes `checksum`, writes `config_versions`, then switches the active version.
|
||||
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
|
||||
## Verify Results
|
||||
|
||||
Check that the node is online, the node version matches the active version, the latest apply log succeeded, and the version page marks the new version as active.
|
||||
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.
|
||||
|
||||
+40
-9
@@ -1,12 +1,43 @@
|
||||
# Guide
|
||||
# Guide Overview
|
||||
|
||||
This section helps operators take OpenFlare from first boot to the first working reverse proxy configuration.
|
||||
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.
|
||||
|
||||
Suggested order:
|
||||
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.
|
||||
|
||||
1. [Quick Start](./quick-start.md): run Server with Docker Compose and complete the first login.
|
||||
2. [Deployment](./deployment.md): review production deployment, Agent install, validation, and upgrade.
|
||||
3. [Run Server](./server.md): learn source startup, frontend build, and Swagger access.
|
||||
4. [Connect Agent](./agent.md): use `agent_token` or `discovery_token` to bring a node online.
|
||||
5. [Publish First Site](./first-site.md): create a site configuration, publish it, and verify node application.
|
||||
6. [Upgrade and Maintenance](./upgrade.md): understand upgrade, uninstall, validation, and maintenance entry points.
|
||||
## 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.
|
||||
|
||||
+157
-15
@@ -1,10 +1,32 @@
|
||||
# Quick Start
|
||||
|
||||
The minimal OpenFlare setup contains one Server and at least one Agent. Server owns the web console, release versions, and node state. Agent runs on proxy nodes and applies OpenResty configuration.
|
||||
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.
|
||||
|
||||
## Run Server
|
||||
The minimum running unit of OpenFlare consists of:
|
||||
|
||||
Docker Compose with PostgreSQL is recommended:
|
||||
| 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:
|
||||
@@ -32,20 +54,36 @@ services:
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
SESSION_SECRET: replace-with-random-string
|
||||
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
|
||||
```
|
||||
|
||||
Open `http://localhost:3000`.
|
||||
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:
|
||||
|
||||
@@ -53,11 +91,54 @@ Default credentials:
|
||||
| --- | --- |
|
||||
| `root` | `123456` |
|
||||
|
||||
Change the default password immediately after first login.
|
||||
Please change the default password immediately after your first login.
|
||||
|
||||
## Connect a Node
|
||||
## 2. Prepare Agent Token
|
||||
|
||||
Prepare a `discovery_token` or node-specific `agent_token` in the console, then run the install script on the node.
|
||||
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 -- \
|
||||
@@ -65,13 +146,74 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
|
||||
--agent-token YOUR_AGENT_TOKEN
|
||||
```
|
||||
|
||||
The script installs Agent under `/opt/openflare-agent`, creates `openflare-agent.service`, and uses Docker OpenResty unless a local `openresty_path` is configured.
|
||||
The script defaults to:
|
||||
|
||||
## Publish the First Configuration
|
||||
| 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 |
|
||||
|
||||
1. Create a site configuration with a domain and origin URL.
|
||||
2. Preview the release or check the diff before publishing.
|
||||
3. Activate the new version.
|
||||
4. Wait for Agent to discover and apply it through heartbeat.
|
||||
Verify the Agent service status:
|
||||
|
||||
Version numbers use `YYYYMMDD-NNN`. Historical versions are immutable; rollback reactivates an old version.
|
||||
```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.
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# Run Server
|
||||
|
||||
OpenFlare Server is the Gin + GORM control plane. It owns the web console, management API, Agent API, configuration rendering, release publishing, and state storage.
|
||||
|
||||
## Requirements
|
||||
|
||||
| Item | Requirement |
|
||||
| --- |-------------------------------------------------------|
|
||||
| Go | `1.25+` |
|
||||
| Node.js | `18+` |
|
||||
| Database | Writable SQLite path or reachable PostgreSQL instance |
|
||||
|
||||
Set `SESSION_SECRET` explicitly in production and prefer PostgreSQL.
|
||||
|
||||
## Build Frontend
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## Run from Source
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
export SESSION_SECRET='replace-with-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
# Optional PostgreSQL:
|
||||
# export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
go run .
|
||||
```
|
||||
|
||||
The default port is `3000`.
|
||||
|
||||
## Swagger
|
||||
|
||||
After logging in, open:
|
||||
|
||||
```text
|
||||
http://localhost:3000/swagger/index.html
|
||||
```
|
||||
|
||||
Regenerate Swagger files locally:
|
||||
|
||||
```bash
|
||||
go install github.com/swaggo/swag/cmd/swag@v1.16.4
|
||||
cd openflare_server
|
||||
swag init -g main.go -o docs
|
||||
```
|
||||
@@ -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.
|
||||
@@ -1,47 +0,0 @@
|
||||
# Upgrade and Maintenance
|
||||
|
||||
## Server Upgrade
|
||||
|
||||
Root users can check and upgrade stable Server releases from the console header. Manual binary upload is also supported.
|
||||
|
||||
Preview releases require manual selection. Stable releases are recommended for production.
|
||||
|
||||
## Agent Upgrade
|
||||
|
||||
Agents follow stable releases by default. Preview upgrades must be triggered manually.
|
||||
|
||||
The install script can be re-run for reinstall or upgrade:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
## Data Maintenance
|
||||
|
||||
The settings page controls observability cleanup:
|
||||
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| `DatabaseAutoCleanupEnabled` | Enable daily cleanup |
|
||||
| `DatabaseAutoCleanupRetentionDays` | Retention days, minimum 1 |
|
||||
|
||||
When enabled, Server cleans access logs, metric snapshots, and request reports at 03:00 every day.
|
||||
|
||||
## Validation Commands
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
pnpm build
|
||||
```
|
||||
@@ -0,0 +1,156 @@
|
||||
# Basic Usage
|
||||
|
||||
You will learn: What website configurations, origins, certificates, versions, nodes, and observability are in OpenFlare, and the recommended sequence of operations during daily usage.
|
||||
|
||||
OpenFlare does not directly modify Nginx/OpenResty configurations on nodes online. What you modify in the management console is control plane data; only after publishing and activating a new version will the Agent pull the complete configuration and apply it to the nodes.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
| Concept | Description |
|
||||
| --- | --- |
|
||||
| Website Config | The aggregate object for reverse proxy rules. One website configuration can bind one or more domains. |
|
||||
| Primary Domain | The first domain in the `domains` list, used as the main display domain for the website. |
|
||||
| Origin | The upstream address accessed by the reverse proxy, e.g., `http://10.0.0.10:8080`. |
|
||||
| Config Version | An immutable snapshot of the complete OpenResty configuration generated upon publishing. |
|
||||
| Active Version | The globally effective configuration version. All nodes consume the same active version by default. |
|
||||
| Agent | The node-side process responsible for registration, heartbeats, sync, validation, reloads, and rollbacks on failure. |
|
||||
|
||||
## Recommended Operation Sequence
|
||||
|
||||
When publishing a reverse proxy configuration in daily operations, the following sequence is recommended:
|
||||
|
||||
1. Confirm that at least one Agent node is online.
|
||||
2. Add or select an origin address.
|
||||
3. Create a website configuration, entering the domain, origin, and site-level configurations.
|
||||
4. If HTTPS is required, upload or select a certificate and bind it by domain.
|
||||
5. Preview the configuration or review the change summary.
|
||||
6. Publish and activate the new version.
|
||||
7. Verify the application result in the node details and application logs.
|
||||
|
||||
## Create Website Configuration
|
||||
|
||||
A website configuration requires at least:
|
||||
|
||||
| Field | Requirement |
|
||||
| --- | --- |
|
||||
| Website Name | Business unique identifier; the primary domain is usually used if left blank |
|
||||
| Domain | At least one domain, where the first is the primary domain; any domain can belong to only one website globally |
|
||||
| Origin Address | A valid `http://` or `https://` address |
|
||||
| Enabled Status | Only enabled website configurations will participate in publishing and rendering |
|
||||
|
||||
Example:
|
||||
|
||||
| Field | Example |
|
||||
| --- | --- |
|
||||
| Website Name | `docs` |
|
||||
| Domain | `docs.example.com` |
|
||||
| Origin Address | `http://10.0.0.10:8080` |
|
||||
| Back-to-source Host | `docs.internal.example.com` |
|
||||
|
||||
Upstream Address Rules:
|
||||
|
||||
* A single upstream can carry a base path or query, e.g., `https://app.example.com/base?from=openflare`.
|
||||
* When multiple upstreams are used for load balancing, each upstream must be a pure `scheme://host[:port]`.
|
||||
* Multiple upstreams in the same rule must use the same protocol.
|
||||
|
||||
## Manage Origins
|
||||
|
||||
Origins act as a lightweight directory to reuse common upstream addresses. After a website configuration links with an origin, it still stores a renderable snapshot of the `origin_url`, ensuring that historic configuration versions can be re-rendered and rolled back independently.
|
||||
|
||||
Recommended Practices:
|
||||
|
||||
* Maintain internal service addresses that are frequently reused as Origins.
|
||||
* After modifying an origin directory, check if published website configurations need their origin snapshots updated.
|
||||
* Use preview or diff to verify rendering results before publishing.
|
||||
|
||||
## Enable HTTPS
|
||||
|
||||
HTTPS is bound by domain rather than being forced across the entire website.
|
||||
|
||||
Operation Sequence:
|
||||
|
||||
1. Upload or host certificates in the Certificate Management section.
|
||||
2. Edit the website configuration and select certificates for domains requiring HTTPS.
|
||||
3. Domains without a bound certificate will remain HTTP and will not be automatically placed in a `443 ssl` server block.
|
||||
4. Publish and activate the new version.
|
||||
|
||||
If a website contains multiple domains, the Server groups and renders the HTTPS configuration by certificate during publishing while keeping these domains within the same website snapshot.
|
||||
|
||||
## Configure WAF & PoW
|
||||
|
||||
Security protection is centrally accessed via the **WAF** link in the side navigation bar:
|
||||
|
||||
* The WAF page maintains global and custom rule groups. Global rule groups always apply to all websites; custom rule groups can bind websites directly in the group settings or inside the `WAF` section of the website details.
|
||||
* Clicking **Manage IP Groups** on the WAF page opens the independent IP Groups section. Manual IP groups store IPs/CIDR blocks directly; automatic IP groups evaluate Expr rules against request logs periodically to update members; subscription IP groups periodically sync from remote text or JSON feeds.
|
||||
* The Auto IP Group page provides two presets: requests count > 100 and 404 ratio >= 80% from a single IP; or IP-host direct access count > 50 and direct access ratio > 50% from a single IP. You can click **Test Rule** to preview IPs matching the log window before saving, and click **Execute Now** to update the group members instantly after saving. The syntax is detailed in [WAF Auto IP Group Expressions](./waf-ip-group-expr.md).
|
||||
* In the blacklist/whitelist settings of a WAF rule group, you can add IPs/CIDR blocks directly or reference existing IP groups. The published version snapshot only contains referenced IP group IDs; the Agent synchronizes IP group members via checksum differentials and WebSocket real-time broadcasts.
|
||||
* `PoW` is a configuration Tab in the rule group, located between `Blacklist/Whitelist` and `Block Interception`. It reuses the site's existing PoW execution logic, allowing current PoW parameters to apply to all websites or only those bound to the current rule group.
|
||||
* The website details page no longer edits individual PoW rules; it only displays the global WAF rule group and binds custom WAF rule groups. The PoW enablement scopes and rule parameters must be maintained centrally on the WAF pages.
|
||||
|
||||
After WAF rule groups, site bindings, or PoW configurations are modified, you must republish and activate the configuration version to let the Agent pull and apply them to OpenResty. IP group member changes do not require a new version publication; online Agents update incrementally via WebSockets, while offline or non-WebSocket Agents synchronize via checksum differentials in the next heartbeat.
|
||||
|
||||
For detailed information on WAF security configurations and evaluation principles, see [WAF Security Protection](./waf-usage.md).
|
||||
|
||||
## Publish, Activate & Rollback
|
||||
|
||||
Standard Pipeline:
|
||||
|
||||
```text
|
||||
Modify config -> 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, and certificate assets, rendering the complete configuration and calculating its `checksum`.
|
||||
|
||||
Rolling back does not modify historic versions; it simply re-activates an older version. Once the Agent detects a change in the active version, it pulls and applies it following the standard sync flow.
|
||||
|
||||
## View Nodes & Observability
|
||||
|
||||
The Nodes section is designed to answer three questions:
|
||||
|
||||
| Question | Where to check |
|
||||
| --- | --- |
|
||||
| Is the node online? | Node List or Node Details |
|
||||
| Which version is currently running? | Current Version in Node Details |
|
||||
| Did the most recent application succeed? | Application Logs |
|
||||
|
||||
The node IP is automatically filled by Agent registration and heartbeats by default. If you manually enter or modify the IP in the management console, the node edit page defaults to "Lock Node IP"; when enabled, Agent reports will not override this IP. Disabling the lock restores auto-update logic in the next heartbeat or WebSocket state report.
|
||||
|
||||
Traffic Analytics and Resource Snapshots provide basic observability. OpenFlare only retains access details within a controlled time window, and is not positioned as a general logging platform. If you require long-term log indexing, integrate an independent logging system.
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Add a Reverse Proxy for an Internal Service
|
||||
|
||||
1. Verify that the origin service is reachable from the Agent node.
|
||||
2. Add a website configuration in the management console.
|
||||
3. Enter the domain, e.g., `app.example.com`.
|
||||
4. Enter the origin, e.g., `http://10.0.0.20:8080`.
|
||||
5. Publish and activate the version.
|
||||
6. Verify the domain on the Agent node or from a browser.
|
||||
|
||||
> [!TIP]
|
||||
> If your origin server is deployed internally without a public IP and is unreachable by the Agent, use the intranet penetration tunnel feature to map your service. For detailed instructions, see [Tunnel & Intranet Penetration](./tunnel-usage.md).
|
||||
|
||||
### Enable HTTPS for an Existing Domain
|
||||
|
||||
1. Prepare a certificate covering the domain.
|
||||
2. Upload or create a certificate record in Certificate Management.
|
||||
3. Edit the website configuration and select the certificate for the domain.
|
||||
4. Publish and activate the version.
|
||||
5. Verify the certificate chain and status code in a browser or via `curl -I https://your-domain`.
|
||||
|
||||
### Roll Back a Failed Publication
|
||||
|
||||
1. Open the Configuration Versions page.
|
||||
2. Locate the last known good version.
|
||||
3. Re-activate that version.
|
||||
4. Check the node application logs to verify that the Agent applied the old version.
|
||||
5. Fix the configuration issues before publishing a new version.
|
||||
|
||||
## Recommended Practices
|
||||
|
||||
* Explicitly configure `SESSION_SECRET` and prefer PostgreSQL in production.
|
||||
* Review the preview or diff after modifying a website configuration before publishing.
|
||||
* Check the node details and application logs after every publication.
|
||||
* Maintain a stable network path from Agent to Server in multi-node deployments.
|
||||
* Never manually modify OpenResty configurations managed by OpenFlare on the node; these files will be overwritten in the next publication.
|
||||
@@ -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.
|
||||
+18
-15
@@ -3,30 +3,33 @@ layout: home
|
||||
|
||||
hero:
|
||||
name: OpenFlare
|
||||
text: Self-hosted OpenResty control plane
|
||||
tagline: Manage reverse proxy rules, configuration releases, node sync, TLS certificates, and basic observability.
|
||||
text: Open-source CDN Orchestration & Edge Security Platform
|
||||
tagline: Supports reverse proxy, centralized configuration synchronization, secure intranet penetration (Tunnels), dynamic WAF protection, and anti-CC challenges.
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Quick Start
|
||||
link: /en/guide/quick-start
|
||||
- theme: alt
|
||||
text: Design Boundary
|
||||
text: Design Boundaries
|
||||
link: /en/design/
|
||||
- theme: alt
|
||||
text: GitHub
|
||||
link: https://github.com/Rain-kl/OpenFlare
|
||||
|
||||
features:
|
||||
- icon: 🧭
|
||||
title: Unified Control Plane
|
||||
details: Manage sites, domains, origins, certificates, nodes, and release state in one console.
|
||||
- icon: 🚀
|
||||
title: Immutable Releases
|
||||
details: Each publish creates a full OpenResty configuration snapshot that can be previewed, activated, and rolled back.
|
||||
- icon: 🔁
|
||||
title: Agent Automation
|
||||
details: Nodes pull, validate, reload, and roll back to the last runnable configuration on failure.
|
||||
- icon: 📊
|
||||
title: Basic Observability
|
||||
details: Includes request rollups, access analytics, resource snapshots, health events, and node details.
|
||||
- icon: 🛰️
|
||||
title: Centralized Config Sync
|
||||
details: Sync configurations across all nodes in real time via WebSockets and heartbeats with sub-second hot reload. Instantly retrieve alerts and statuses.
|
||||
- icon: 🌐
|
||||
title: Distributed CDN Orchestration
|
||||
details: Orchestrate scattered and independent OpenResty nodes into a highly collaborative CDN fleet with website-level multi-domain aggregation and load balancing.
|
||||
- icon: 🚇
|
||||
title: Secure Intranet Penetration (Tunnels)
|
||||
details: An open-source alternative to Cloudflare Tunnels. Expose local intranet services securely to the public network without a public IP or open inbound ports.
|
||||
- icon: 🛡️
|
||||
title: Edge WAF Protection
|
||||
details: Dynamic WAF rules with differential syncing of IP groups to Lua shared memory without Nginx reloads, plus country-level regional access control.
|
||||
- icon: 🧩
|
||||
title: Anti-CC & Bot Defense (PoW)
|
||||
details: Built-in high-performance client-side cryptographic Proof of Work challenges (similar to Turnstile) to intercept botnets and scrapers at the edge.
|
||||
---
|
||||
|
||||
+124
-13
@@ -1,10 +1,12 @@
|
||||
# API Conventions
|
||||
|
||||
Management API and Agent API both use JSON.
|
||||
You will learn: The response structure, path conventions, authentication methods, and Swagger entrance for the OpenFlare Admin API and Agent API.
|
||||
|
||||
## Response Shape
|
||||
Both the OpenFlare Admin API and Agent API communicate using JSON.
|
||||
|
||||
Success and failure responses should include a clear `message`:
|
||||
## Response Structure
|
||||
|
||||
Both successful and failed API responses must return a clear `message`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -14,31 +16,140 @@ Success and failure responses should include a clear `message`:
|
||||
}
|
||||
```
|
||||
|
||||
## Paths
|
||||
## Path Conventions
|
||||
|
||||
| Type | Convention |
|
||||
| Category | Convention |
|
||||
| --- | --- |
|
||||
| Management API | Authenticated by management Session |
|
||||
| Agent API | Fixed under `/api/agent/*` |
|
||||
| Read-only endpoints | `GET` |
|
||||
| Mutating endpoints | `POST` |
|
||||
| Admin API | Authenticated via the Admin Session |
|
||||
| Agent API | Located strictly under `/api/agent/*` |
|
||||
| Relay API | Located strictly under `/api/relay/*`, authenticated via `X-Agent-Token` (reusing the Agent's token) |
|
||||
| OpenFlared API | Located strictly under `/api/flared/*`, authenticated via `X-Tunnel-Token` (dedicated tunnel_token) |
|
||||
| Read-only APIs | Use the `GET` method |
|
||||
| Mutating APIs | Use the `POST` method |
|
||||
|
||||
## WAF IP Group APIs
|
||||
|
||||
The Admin WAF IP Group APIs require Admin Session authentication:
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/waf/ip-groups` | Query IP groups list |
|
||||
| `GET` | `/api/waf/ip-groups/:id` | Query a single IP group |
|
||||
| `POST` | `/api/waf/ip-groups` | Create a new IP group |
|
||||
| `POST` | `/api/waf/ip-groups/test` | Test automatic IP group Expr rules; returns matching IPs in the lookback window without persisting the config |
|
||||
| `POST` | `/api/waf/ip-groups/:id/update` | Update an existing IP group |
|
||||
| `POST` | `/api/waf/ip-groups/:id/delete` | Delete an IP group; denied if currently referenced by any rule group |
|
||||
| `POST` | `/api/waf/ip-groups/:id/sync` | Manually sync subscription IP groups or execute automatic IP group aggregation |
|
||||
|
||||
The IP group `type` supports `manual`, `automatic`, and `subscription`. The `auto_config` parameter for automatic IP groups 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"
|
||||
},
|
||||
{
|
||||
"name": "Single IP Direct IP Access Mismatch",
|
||||
"expr": "ip_host_count > 50 && ip_host_ratio > 0.5"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Automatic rules evaluate Expr boolean expressions against metrics aggregated on a per-client-IP basis. The available metrics include `ip`, `request_count`, `status_404_count`, `status_404_ratio`, `ip_host_count`, `ip_host_ratio`, `client_error_count`, `server_error_count`, and `last_seen_unix`. The full syntax is detailed in [WAF Auto IP Group Expressions](../guide/waf-ip-group-expr.md).
|
||||
|
||||
Subscription formats support `text` and `json`: plain text parsing resolves one IP or CIDR per line, ignoring empty lines and comments starting with `#`; JSON parsing decodes arrays, reading the root array by default.
|
||||
|
||||
## Authentication
|
||||
|
||||
Management endpoints reuse the existing login, role, and Session system.
|
||||
The Admin panel continues to reuse the existing login, role, and Session validation.
|
||||
|
||||
Agent requests use the node-specific `agent_token`. First-time registration can use a global `discovery_token`. The header is:
|
||||
Agent requests must carry the node-specific `agent_token` (except for first-time registration, which can use the global `discovery_token`). The header is formatted as:
|
||||
|
||||
```http
|
||||
X-Agent-Token: <token>
|
||||
```
|
||||
|
||||
Do not log full tokens.
|
||||
### Agent WAF IP Group Synchronization
|
||||
|
||||
The Agent heartbeat payload can carry local WAF IP group checksums:
|
||||
|
||||
```json
|
||||
{
|
||||
"waf_ip_group_checksums": {
|
||||
"1": "sha256..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The Server evaluates the checksums against active configurations, returning mismatched IP groups in the heartbeat response:
|
||||
|
||||
```json
|
||||
{
|
||||
"waf_ip_groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Auto Blacklist",
|
||||
"type": "automatic",
|
||||
"enabled": true,
|
||||
"ip_list": ["203.0.113.10"],
|
||||
"checksum": "sha256..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, the Agent can proactively request differential updates upon applying a new configuration version:
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/agent/waf/ip-groups/sync` | Returns mismatched WAF IP groups based on Agent-supplied `ids` and `checksums` |
|
||||
|
||||
When an IP group is updated on the Server, connected Agents receive a WebSocket push containing `type = "waf_ip_groups"` with the changed IP groups array as payload. The Agent updates only the changed groups incrementally.
|
||||
|
||||
## OpenFlared API
|
||||
|
||||
The OpenFlared client communicates with the Server via a dedicated `tunnel_token`, completely decoupled from the Agent authentication system. All endpoints require `X-Tunnel-Token` authentication; requests are denied with `403` if the token is invalid.
|
||||
|
||||
| Method | Path | Description |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/api/flared/heartbeat` | Client heartbeat, updates online status and retrieves active tunnel config version summaries |
|
||||
| `GET` | `/api/flared/config/active` | Pulls the complete tunnel routing configuration (relay list + frpc proxy definitions) |
|
||||
| `POST` | `/api/flared/apply-log` | Reports configuration application results (success / warning / failed) |
|
||||
| `GET` | `/api/flared/ws` | Upgrades to a WebSocket connection for real-time `active_config` pushes |
|
||||
|
||||
Heartbeat request example:
|
||||
|
||||
```http
|
||||
POST /api/flared/heartbeat
|
||||
X-Tunnel-Token: <tunnel_token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"client_version": "v0.2.0",
|
||||
"frp_version": "0.61.0",
|
||||
"tunnel_status": "running",
|
||||
"connected_relays": [
|
||||
{ "relay_node_id": "node-relay-1", "status": "healthy", "proxy_count": 3 }
|
||||
],
|
||||
"current_version": "v1",
|
||||
"current_checksum": "sha256..."
|
||||
}
|
||||
```
|
||||
|
||||
The heartbeat response returns the `active_config` summary and `tunnel_settings` (containing runtime settings like heartbeat intervals and WebSocket upgrade switches). When a new configuration version is published, the Server broadcasts a message `type = "active_config"` with the version summary as payload to all connected Clients over WebSockets, prompting them to fetch and apply the config immediately.
|
||||
|
||||
Full tokens must never be logged.
|
||||
|
||||
## Swagger
|
||||
|
||||
After logging in:
|
||||
Once logged into the management console, the Swagger page is accessible at:
|
||||
|
||||
```text
|
||||
/swagger/index.html
|
||||
```
|
||||
|
||||
The Swagger definition file is stored in `openflare-server/docs`, generated by `swag init`.
|
||||
|
||||
@@ -1,54 +1,113 @@
|
||||
# Commands and Scripts
|
||||
# CLI Commands
|
||||
|
||||
You will learn: Common commands for starting, building, testing, installing, and uninstalling the OpenFlare Server, Admin Frontend, Agent, Swagger, and Documentation site.
|
||||
|
||||
## Server
|
||||
|
||||
Start from source:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
cd openflare-server
|
||||
export SESSION_SECRET='replace-with-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
go run .
|
||||
```
|
||||
|
||||
Specify listening port and logging directory:
|
||||
|
||||
```bash
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
cd openflare-server
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
## Frontend
|
||||
|
||||
Development:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
cd openflare-server/web
|
||||
pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Build static assets:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
cd openflare-server/web
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Linting and testing checks:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
```
|
||||
|
||||
## Agent
|
||||
|
||||
Run from source:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
cd openflare-agent
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
Compile:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
cd openflare-agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
cd openflare-agent
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
## Relay (Server-side)
|
||||
|
||||
Run from source:
|
||||
|
||||
```bash
|
||||
cd openflare-relay
|
||||
go run ./cmd -config /path/to/relay.json
|
||||
```
|
||||
|
||||
Compile:
|
||||
|
||||
```bash
|
||||
cd openflare-relay
|
||||
go build -o openflare-relay ./cmd
|
||||
```
|
||||
|
||||
## OpenFlared (Client-side)
|
||||
|
||||
Run from source:
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go run ./cmd -config /path/to/flared.json
|
||||
```
|
||||
|
||||
Compile:
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go build -o openflared ./cmd
|
||||
```
|
||||
|
||||
## Install Agent
|
||||
|
||||
```bash
|
||||
@@ -62,3 +121,29 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
## Swagger
|
||||
|
||||
Regenerate Swagger documentation:
|
||||
|
||||
```bash
|
||||
go install github.com/swaggo/swag/cmd/swag@v1.16.4
|
||||
cd openflare-server
|
||||
swag init -g main.go -o docs
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
Local preview:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Build:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
pnpm build
|
||||
```
|
||||
|
||||
@@ -1,72 +1,370 @@
|
||||
# Configuration
|
||||
# Configuration Options
|
||||
|
||||
## Server CLI Flags
|
||||
You will learn: What configuration sources are supported by OpenFlare Server, frontend builds, and Agents; what the default configuration values are; and how to configure common deployment combinations.
|
||||
|
||||
| Flag | Purpose | Default |
|
||||
This document aggregates the currently supported configuration options for OpenFlare Server and Agent in version `1.0.0`, keeping only running parameters that are currently active.
|
||||
|
||||
## Configuration Sources
|
||||
|
||||
The Server supports three types of configuration sources:
|
||||
|
||||
1. CLI arguments.
|
||||
2. Environment variables.
|
||||
3. Runtime configurations in the database `options` table.
|
||||
|
||||
The Agent supports:
|
||||
|
||||
1. The `-config` CLI argument.
|
||||
2. The `agent.json` configuration file.
|
||||
3. A small set of environment variables for overriding logs and settings.
|
||||
|
||||
The Relay (Server-side) supports:
|
||||
|
||||
1. The `-config` CLI argument.
|
||||
2. The `relay.json` configuration file.
|
||||
3. Persistent environment variables for overriding runtime flags.
|
||||
|
||||
The Client (Intranet Client) supports:
|
||||
|
||||
1. The `-config` CLI argument.
|
||||
2. The `flared.json` configuration file.
|
||||
3. Startup overrides and logging environment variables.
|
||||
|
||||
## Configuration File Locations
|
||||
|
||||
| Component | Default Location | Description |
|
||||
| --- | --- | --- |
|
||||
| `--port` | Server listen port | `3000` |
|
||||
| `--log-dir` | Log directory | empty |
|
||||
| `--version` | Print version and exit | `false` |
|
||||
| `--help` | Print help and exit | `false` |
|
||||
| Server SQLite | `openflare.db` | Can be customized via `SQLITE_PATH` |
|
||||
| Agent Config | `./agent.json` | Can be specified via `-config` |
|
||||
| One-Click Agent | `/opt/openflare-agent/agent.json` | Generated by the installation script by default |
|
||||
| Agent Data Dir | `data` in the config folder | Can be customized via `data_dir` |
|
||||
| Relay Config | `./relay.json` | Can be specified via `-config` |
|
||||
| One-Click Relay | `/opt/openflare-relay/relay.json` | Generated by the installation script by default |
|
||||
| Client Config | `./flared.json` | Can be specified via `-config` |
|
||||
| One-Click Client | `/opt/openflared/flared.json` | Generated by the installation script by default |
|
||||
|
||||
## Server CLI Arguments
|
||||
|
||||
```bash
|
||||
cd openflare-server
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `--port` | Port the Server listens on | `3000` |
|
||||
| `--log-dir` | Directory to output logs | Empty (stdout) |
|
||||
| `--version` | Outputs current version and exits | `false` |
|
||||
| `--help` | Outputs help information and exits | `false` |
|
||||
|
||||
## Server Environment Variables
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `PORT` | Server listen port | `3000` |
|
||||
| `GIN_MODE` | Gin mode | release unless `debug` |
|
||||
| `LOG_LEVEL` | Log level | `info` |
|
||||
| `SESSION_SECRET` | Session signing secret | random on startup |
|
||||
| `SQLITE_PATH` | SQLite database path | `openflare.db` |
|
||||
| `DSN` | PostgreSQL DSN, preferred over SQLite | empty |
|
||||
| `SQL_DSN` | Legacy PostgreSQL DSN, lower priority than `DSN` | empty |
|
||||
| `REDIS_CONN_STRING` | Redis connection string | empty |
|
||||
| `UPLOAD_PATH` | Upload directory | `upload` |
|
||||
| `AGENT_TOKEN` | Legacy global Agent token | empty |
|
||||
| `PORT` | Port the Server listens on | `3000` |
|
||||
| `GIN_MODE` | Gin framework running mode | Defaults to release unless `debug` |
|
||||
| `LOG_LEVEL` | Logging level | `info` |
|
||||
| `SESSION_SECRET` | Session signing key | Randomly generated on startup |
|
||||
| `SQLITE_PATH` | SQLite database file path | `openflare.db` |
|
||||
| `DSN` | PostgreSQL DSN (takes precedence over SQLite) | Empty |
|
||||
| `SQL_DSN` | Legacy PostgreSQL DSN (lower priority than `DSN`) | Empty |
|
||||
| `REDIS_CONN_STRING` | Redis connection string | Empty |
|
||||
| `AGENT_TOKEN` | Legacy global Agent Token | Empty |
|
||||
|
||||
When `DSN` and `SQL_DSN` both exist, `DSN` wins. PostgreSQL is preferred when configured. If PostgreSQL is empty and a local SQLite file exists, Server migrates SQLite data at startup.
|
||||
Notes:
|
||||
|
||||
## Frontend Build Variables
|
||||
|
||||
| Variable | Purpose | Default |
|
||||
| --- | --- | --- |
|
||||
| `NEXT_PUBLIC_API_BASE_URL` | Frontend API base path | `/api` |
|
||||
| `NEXT_PUBLIC_APP_VERSION` | Displayed frontend version | `dev` |
|
||||
| `NEXT_DEV_BACKEND_URL` | Local dev backend proxy target | `http://127.0.0.1:3000` |
|
||||
* If both `DSN` and `SQL_DSN` exist, `DSN` is prioritized.
|
||||
* If either `DSN` or `SQL_DSN` coexist with `SQLITE_PATH`, PostgreSQL is prioritized.
|
||||
* If the target PostgreSQL database is empty and a local SQLite file exists at `SQLITE_PATH`, the Server automatically migrates SQLite data table-by-table on startup.
|
||||
* `SESSION_SECRET` must be explicitly configured in production.
|
||||
* If `REDIS_CONN_STRING` is unconfigured, co-located features fall back to in-memory implementations.
|
||||
|
||||
## Runtime Options
|
||||
|
||||
The settings page maintains these hot-updatable options:
|
||||
The following options are maintained in the admin settings page and support hot reloading:
|
||||
|
||||
| Option | Purpose | Default |
|
||||
| Parameter | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `AgentHeartbeatInterval` | Agent heartbeat interval in milliseconds | `10000` |
|
||||
| `NodeOfflineThreshold` | Node offline threshold in milliseconds | `120000` |
|
||||
| `AgentUpdateRepo` | Agent update repository | `Rain-kl/OpenFlare` |
|
||||
| `GeoIPProvider` | Node/IP region provider | `ipinfo` |
|
||||
| `DatabaseAutoCleanupEnabled` | Enable daily observability cleanup | `false` |
|
||||
| `DatabaseAutoCleanupRetentionDays` | Retention days | `30` |
|
||||
| `AgentHeartbeatInterval` | Heartbeat interval for Agents (ms) | `10000` |
|
||||
| `AgentWebsocketUpgradeEnabled` | Toggles WebSocket upgrades after successful HTTP heartbeat | `true` |
|
||||
| `NodeOfflineThreshold` | Threshold duration to mark a node offline (ms) | `120000` |
|
||||
| `AgentUpdateRepo` | GitHub repository for Agent self-updates | `Rain-kl/OpenFlare` |
|
||||
| `GeoIPProvider` | Geolocation resolution provider | `ipinfo` |
|
||||
| `DatabaseAutoCleanupEnabled` | Toggles daily automatic cleanup of observability logs | `false` |
|
||||
| `DatabaseAutoCleanupRetentionDays` | Data retention duration in days, minimum 1 day | `30` |
|
||||
| `GlobalApiRateLimitNum` / `GlobalApiRateLimitDuration` | Global API rate limit count / window | `300` / `180` |
|
||||
| `GlobalWebRateLimitNum` / `GlobalWebRateLimitDuration` | Global Web rate limit count / window | `300` / `180` |
|
||||
| `CriticalRateLimitNum` / `CriticalRateLimitDuration` | Sensitive API rate limit count / window | `100` / `1200` |
|
||||
|
||||
OpenResty performance and cache options are also stored in the Option table, including `OpenRestyWorkerProcesses`, `OpenRestyWorkerConnections`, `OpenRestyProxyConnectTimeout`, `OpenRestyProxyReadTimeout`, `OpenRestyCacheEnabled`, `OpenRestyCachePath`, and `OpenRestyCacheMaxSize`.
|
||||
Notes:
|
||||
|
||||
## Agent Configuration
|
||||
* When `DatabaseAutoCleanupEnabled` is enabled, the Server deletes `node_access_logs`, `node_metric_snapshots`, and `node_request_reports` daily at 3:00 AM.
|
||||
* `DatabaseAutoCleanupRetentionDays` must be greater than or equal to 1.
|
||||
* Leaving retention days blank during a manual trigger in the console deletes all historic logs instantly.
|
||||
* The GitHub Release in `AgentUpdateRepo` must contain a matching `.sha256` checksum file for every Agent binary (e.g., `openflare-agent-linux-amd64.sha256`); the Agent validates this checksum before replacing the local executable.
|
||||
* Third-party logins no longer use `GitHubOAuthEnabled`, `GitHubClientId`, and `GitHubClientSecret` as main configuration entrypoints; these legacy options are used only for migrating default GitHub credentials during upgrades.
|
||||
* The legacy WeChat login options are kept for backward compatibility, but the option page no longer edits them.
|
||||
* Legacy Cloudflare Turnstile options and validation logic are retained and will work normally.
|
||||
|
||||
Agent supports the `-config` CLI flag, an `agent.json` file, and the `LOG_LEVEL` environment variable.
|
||||
## OpenResty Parameters
|
||||
|
||||
| Field | Purpose | Required | Default / behavior |
|
||||
OpenResty performance and caching parameters are managed in the `options` table, including:
|
||||
|
||||
* `OpenRestyWorkerProcesses`
|
||||
* `OpenRestyWorkerConnections`
|
||||
* `OpenRestyWorkerRlimitNofile`
|
||||
* `OpenRestyKeepaliveTimeout`
|
||||
* `OpenRestyProxyConnectTimeout`
|
||||
* `OpenRestyProxySendTimeout`
|
||||
* `OpenRestyProxyReadTimeout`
|
||||
* `OpenRestyProxyBufferingEnabled`
|
||||
* `OpenRestyGzipEnabled`
|
||||
* `OpenRestyCacheEnabled`
|
||||
* `OpenRestyCachePath`
|
||||
* `OpenRestyCacheMaxSize`
|
||||
|
||||
These parameters must be validated, saved, and rendered structurally.
|
||||
|
||||
Constraints:
|
||||
|
||||
* The console no longer exposes `resolver` settings.
|
||||
* Upstreams are rendered uniformly as named `upstream` blocks with keepalive enabled.
|
||||
* Single upstreams carrying a base path or query have their URI correctly appended in `proxy_pass`.
|
||||
* Multi-upstreams must be pure `scheme://host[:port]` using the same protocol within a single rule.
|
||||
* `OpenRestyCacheEnabled` enables cache infrastructure and global defaults; the actual caching matching policies (by URL, suffix, or path) are configured per `proxy_routes`.
|
||||
* The default cache key is `$scheme$host$request_uri`.
|
||||
* Default `keepalive_timeout` is `20` seconds; default `proxy_connect_timeout` is `3` seconds.
|
||||
* The default event model is `epoll` with `multi_accept` enabled.
|
||||
* HTTPS listeners use the independent `http2 on;` directive to avoid deprecation warnings for `listen ... http2` in newer Nginx/OpenResty versions.
|
||||
|
||||
## Frontend Build Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `NEXT_PUBLIC_API_BASE_URL` | Base path for frontend API calls | `/api` |
|
||||
| `NEXT_PUBLIC_APP_VERSION` | Application version shown in the UI | `dev` |
|
||||
| `NEXT_DEV_BACKEND_URL` | Target backend proxied by the local dev server | `http://127.0.0.1:3000` |
|
||||
|
||||
## Agent Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `LOG_LEVEL` | Logging level for the Agent | `info` |
|
||||
| `OPENFLARE_SERVER_URL` | Server URL; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_AGENT_TOKEN` | Node-specific Token; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_DISCOVERY_TOKEN` | Auto-registration Token; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_NODE_NAME` | Node name; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_NODE_IP` | Node IP; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_DATA_DIR` | Agent data directory; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_OPENRESTY_PATH` | Path to OpenResty binary; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_HEARTBEAT_INTERVAL` | Heartbeat interval; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_REQUEST_TIMEOUT` | Request timeout; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_OPENRESTY_OBSERVABILITY_PORT` | Local observability port; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_MMDB_PATH` | WAF GeoIP mmdb path; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_MMDB_UPDATE_INTERVAL` | GeoIP mmdb update interval; overrides `agent.json` | Empty |
|
||||
| `OPENFLARE_MMDB_DOWNLOAD_URL` | GeoIP mmdb download link; overrides `agent.json` | Empty |
|
||||
|
||||
## Agent CLI Arguments
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `-config` | Path to the Agent configuration file | `./agent.json` |
|
||||
|
||||
## Agent Configurations Fields
|
||||
|
||||
| Field | Description | Required | Default Value / Behavior |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | Control plane URL | yes | none |
|
||||
| `agent_token` | Node-specific auth token | one of `agent_token` / `discovery_token` | empty |
|
||||
| `discovery_token` | Global token for first registration | one of `agent_token` / `discovery_token` | empty |
|
||||
| `node_name` | Node name | no | host name |
|
||||
| `node_ip` | Node IP | no | auto-detected |
|
||||
| `openresty_path` | Local OpenResty path | no | empty; Docker mode |
|
||||
| `openresty_container_name` | Docker container name | no | `openflare-openresty` |
|
||||
| `openresty_docker_image` | Docker image | no | `openresty/openresty:alpine` |
|
||||
| `openresty_observability_port` | Local observability port | no | `18081` |
|
||||
| `docker_binary` | Docker binary name or path | no | `docker` |
|
||||
| `data_dir` | Agent data directory | no | `data` under config directory |
|
||||
| `heartbeat_interval` | Heartbeat interval | no | `10000` ms |
|
||||
| `request_timeout` | HTTP timeout | no | `10000` ms |
|
||||
| `server_url` | Control plane URL | Yes | None |
|
||||
| `agent_token` | Node-specific access Token | Mutually exclusive with discovery_token | Empty |
|
||||
| `discovery_token` | Global auto-registration Token | Mutually exclusive with agent_token | Empty |
|
||||
| `node_name` | Node name | No | Hostname |
|
||||
| `node_ip` | Node IP | No | Auto-detect, resolves outbound public IP via realip.cc first, falls back to local adapters |
|
||||
| `openresty_path` | Path to the OpenResty binary | No | `"openresty"` |
|
||||
| `openresty_observability_port` | Observability port for health checks | No | `18081` |
|
||||
| `data_dir` | Agent data directory | No | `data` in the config folder |
|
||||
| `main_config_path` | Write path for Nginx main configuration | No | `data_dir/etc/nginx/nginx.conf` |
|
||||
| `route_config_path` | Write path for route configurations | No | `data_dir/etc/nginx/conf.d/openflare_routes.conf` |
|
||||
| `access_log_path` | Write path for OpenResty access logs | No | `data_dir/var/log/openflare/access.log` |
|
||||
| `cert_dir` | Write directory for SSL certificates | No | `data_dir/etc/nginx/certs` |
|
||||
| `openresty_cert_dir` | Read directory for certificates in Nginx | No | Same as `cert_dir` |
|
||||
| `lua_dir` | Write directory for Lua scripts and assets | No | `data_dir/etc/nginx/lua` |
|
||||
| `openresty_lua_dir` | Read directory for Lua scripts in Nginx | No | Same as `lua_dir` |
|
||||
| `runtime_config_dir` | Write directory for Agent runtime configs | No | `data_dir/etc/openflare` |
|
||||
| `mmdb_path` | WAF GeoIP database file path | No | `data_dir/etc/openflare/GeoLite2-Country.mmdb` |
|
||||
| `mmdb_update_interval` | WAF GeoIP database check interval | No | `86400000` milliseconds |
|
||||
| `mmdb_download_url` | WAF GeoIP database download URL | No | Built-in GeoLite2 Country URL |
|
||||
| `observability_buffer_path` | Buffer path for retry metrics logs | No | `data_dir/var/lib/openflare/observability-buffer.json` |
|
||||
| `observability_replay_minutes` | Lookback window for metric retries | No | `15` |
|
||||
| `state_path` | Path to store local state JSON file | No | `data_dir/var/lib/openflare/agent-state.json` |
|
||||
| `heartbeat_interval` | Heartbeat polling interval | No | `10000` milliseconds |
|
||||
| `request_timeout` | HTTP request timeout duration | No | `10000` milliseconds |
|
||||
|
||||
`heartbeat_interval` and `request_timeout` accept milliseconds or Go duration strings.
|
||||
Notes:
|
||||
|
||||
* `agent_token` and `discovery_token` cannot both be empty.
|
||||
* `heartbeat_interval` and `request_timeout` support integer milliseconds or Go duration strings.
|
||||
* If `AgentWebsocketUpgradeEnabled` is enabled on the Server, the Agent upgrades the HTTP heartbeat to WebSocket; it automatically falls back to HTTP heartbeats if it fails or disconnects.
|
||||
* If `openresty_path` is left blank, the Agent calls `openresty` on the host.
|
||||
* Periodic health checks query `http://127.0.0.1:<openresty_observability_port>/openflare/stub_status` instead of executing `openresty -t`; validation prior to reloads, starts, or rollbacks still runs `openresty -t -c <main_config_path>`.
|
||||
* The Agent initializes and periodically updates `mmdb_path` to support GeoIP region checks; failures to update write warnings and do not disrupt configuration synchronizations.
|
||||
* The Agent boots normally if `agent.json` is missing but environment variables (`OPENFLARE_SERVER_URL` and a Token) are available; environment variables override JSON settings.
|
||||
* If `node_ip` is left blank, the Agent resolves its outbound IP via `https://realip.cc` first, which is suitable for Docker/NAT networks.
|
||||
* If the Agent registers a private `node_ip`, the Server prioritizes saving the public TCP connection IP, preventing NAT adapters from registering internal IPs.
|
||||
* Enabling "Lock Node IP" in the console retains the manual IP; subsequent Agent registration or heartbeats do not overwrite it.
|
||||
|
||||
## Relay Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `LOG_LEVEL` | Logging level for the Relay | `info` |
|
||||
| `OPENFLARE_SERVER_URL` | Server URL; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_AGENT_TOKEN` | Node-specific Token; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_DISCOVERY_TOKEN` | Auto-registration Token; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_NODE_NAME` | Node name; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_NODE_IP` | Node IP; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_DATA_DIR` | Relay data directory; overrides `relay.json` | Empty |
|
||||
| `OPENFLARE_FRPS_PATH` | frps binary path; overrides `relay.json` | Empty |
|
||||
|
||||
## Relay CLI Arguments
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `-config` | Path to the Relay configuration file | `./relay.json` |
|
||||
|
||||
## Relay Configuration Fields
|
||||
|
||||
| Field | Description | Required | Default Value / Behavior |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | Control plane URL | Yes | None |
|
||||
| `agent_token` | Node-specific access Token | Mutually exclusive with discovery_token | Empty |
|
||||
| `discovery_token` | Global auto-registration Token | Mutually exclusive with agent_token | Empty |
|
||||
| `node_name` | Node name | No | Hostname |
|
||||
| `node_ip` | Relay listening IP for tunnel traffic | No | Auto-detect, prioritizes outbound public IP |
|
||||
| `frps_path` | Path to the `frps` binary | No | `frps` (system PATH) |
|
||||
| `data_dir` | Relay runtime data directory | No | `data` in the config folder |
|
||||
| `state_path` | Path to store local state JSON file | No | `data_dir/relay-state.json` |
|
||||
| `heartbeat_interval` | Heartbeat polling interval | No | `10000` milliseconds, supports Go duration strings |
|
||||
| `request_timeout` | HTTP request timeout duration | No | `10000` milliseconds, supports Go duration strings |
|
||||
|
||||
## OpenFlared (Client) Environment Variables
|
||||
|
||||
| Environment Variable | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `LOG_LEVEL` | Logging level for the client | `info` |
|
||||
| `OPENFLARE_SERVER_URL` | Server URL; overrides `flared.json` | Empty |
|
||||
| `OPENFLARE_TUNNEL_TOKEN` | Tunnel access Token; overrides `flared.json` | Empty |
|
||||
| `OPENFLARE_DATA_DIR` | Client data directory; overrides `flared.json` | Empty |
|
||||
| `OPENFLARE_FRPC_PATH` | frpc binary path; overrides `flared.json` | Empty |
|
||||
|
||||
## OpenFlared (Client) CLI Arguments
|
||||
|
||||
| Argument | Description | Default Value |
|
||||
| --- | --- | --- |
|
||||
| `-config` | Path to the client configuration file | `./flared.json` |
|
||||
|
||||
## OpenFlared (Client) Configuration Fields
|
||||
|
||||
| Field | Description | Required | Default Value / Behavior |
|
||||
| --- | --- | --- | --- |
|
||||
| `server_url` | Control plane URL | Yes | None |
|
||||
| `tunnel_token` | Tunnel dedicated access Token | Yes | None |
|
||||
| `frpc_path` | Path to the `frpc` binary | No | `frpc` (system PATH) |
|
||||
| `data_dir` | Client runtime data directory | No | `data` in the config folder |
|
||||
| `state_path` | Path to store local state JSON file | No | `data_dir/flared-state.json` |
|
||||
| `heartbeat_interval` | Heartbeat polling interval | No | `10000` milliseconds, supports Go duration strings |
|
||||
| `sync_interval` | Configuration sync interval | No | `30000` milliseconds, supports Go duration strings |
|
||||
| `request_timeout` | HTTP request timeout duration | No | `10000` milliseconds, supports Go duration strings |
|
||||
|
||||
## Common Configuration Combos
|
||||
|
||||
### Production Server + PostgreSQL
|
||||
|
||||
```bash
|
||||
export SESSION_SECRET='replace-with-a-long-random-string'
|
||||
export DSN='postgres://openflare:replace-with-strong-password@postgres:5432/openflare?sslmode=disable'
|
||||
export GIN_MODE='release'
|
||||
export LOG_LEVEL='info'
|
||||
```
|
||||
|
||||
### Local Server + SQLite
|
||||
|
||||
```bash
|
||||
export SESSION_SECRET='dev-session-secret'
|
||||
export SQLITE_PATH='./openflare-dev.db'
|
||||
export LOG_LEVEL='debug'
|
||||
go run .
|
||||
```
|
||||
|
||||
### Agent + Default OpenResty
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "/opt/openflare-agent/data",
|
||||
"openresty_path": "openresty",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
### Agent + Customized OpenResty Paths
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "/var/lib/openflare-agent",
|
||||
"openresty_path": "/usr/local/openresty/nginx/sbin/openresty",
|
||||
"main_config_path": "/var/lib/openflare-agent/etc/nginx/nginx.conf",
|
||||
"route_config_path": "/var/lib/openflare-agent/etc/nginx/conf.d/openflare_routes.conf",
|
||||
"access_log_path": "/var/lib/openflare-agent/var/log/openflare/access.log",
|
||||
"cert_dir": "/var/lib/openflare-agent/etc/nginx/certs",
|
||||
"lua_dir": "/var/lib/openflare-agent/etc/nginx/lua",
|
||||
"runtime_config_dir": "/var/lib/openflare-agent/etc/openflare",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
### Relay (Server-side) Default Configuration
|
||||
|
||||
`relay.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server:3000",
|
||||
"agent_token": "replace-with-relay-auth-token",
|
||||
"frps_path": "frps",
|
||||
"data_dir": "/opt/openflare-relay/data",
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
### OpenFlared (Client-side) Default Configuration
|
||||
|
||||
`flared.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://your-server:3000",
|
||||
"tunnel_token": "replace-with-tunnel-token",
|
||||
"frpc_path": "frpc",
|
||||
"data_dir": "/opt/openflared/data",
|
||||
"heartbeat_interval": 10000,
|
||||
"sync_interval": 30000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
## Maintenance Rules
|
||||
|
||||
This document must be updated in sync when any of the following change:
|
||||
|
||||
* Server CLI arguments.
|
||||
* Server environment variables.
|
||||
* Agent CLI arguments and configuration parameters.
|
||||
* Relay CLI arguments and configuration parameters.
|
||||
* Client CLI arguments and configuration parameters.
|
||||
* Default values, scopes, or examples of any configuration items.
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# Reference
|
||||
# Reference Manuals
|
||||
|
||||
This section collects stable runtime, API, and repository information for deployment, integration, and troubleshooting.
|
||||
You will learn: Which information belongs to stable reference manuals, and where to look up configurations, commands, APIs, and repository structures.
|
||||
|
||||
This section collects stable information at the runtime, API, and repository layers, suitable for rapid lookup during deployment, integration, and troubleshooting.
|
||||
|
||||
| Page | Content |
|
||||
| --- | --- |
|
||||
| [Configuration](./configuration.md) | Server environment variables, CLI flags, runtime options, and Agent config fields |
|
||||
| [Commands and Scripts](./cli.md) | Startup, build, test, install, and uninstall commands |
|
||||
| [API Conventions](./api.md) | Management API and Agent API response, auth, and path conventions |
|
||||
| [Repository Layout](./repository.md) | Responsibilities of `openflare_server`, `openflare_agent`, `openflare_server/web`, and `docs` |
|
||||
| [Configuration Options](./configuration.md) | Server environment variables, CLI arguments, runtime Options, and Agent configuration parameters |
|
||||
| [CLI Commands](./cli.md) | Common CLI commands for starting, building, testing, installing, and uninstalling |
|
||||
| [API Conventions](./api.md) | Response structures, authentication, and routing paths for Admin and Agent APIs |
|
||||
| [Repository Structure](../design/repository.md) | Scope of responsibilities and folder layering of the Server, Agent, Relay, and Client |
|
||||
| [Deployment & Upgrade](../deployment/) | Server and Agent deployment, configuration, and upgrade guides (dedicated section) |
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# Repository Layout
|
||||
|
||||
| Path | Responsibility |
|
||||
| --- | --- |
|
||||
| `openflare_server` | Gin + GORM + SQLite/PostgreSQL control plane |
|
||||
| `openflare_server/web` | Next.js 15 App Router admin frontend, statically exported and served by Go Server |
|
||||
| `openflare_agent` | Go Agent running on nodes |
|
||||
| `scripts` | Agent install, uninstall, and helper scripts |
|
||||
| `docs` | VitePress docs site, design baseline, development rules, deployment and configuration docs |
|
||||
|
||||
## Server Layers
|
||||
|
||||
| Directory | Responsibility |
|
||||
| --- | --- |
|
||||
| `controller/` | Parse input, call service, return response |
|
||||
| `service/` | Business logic, validation, transactions, rendering |
|
||||
| `model/` | Models, database versioning, migrations |
|
||||
| `router/` | Route registration |
|
||||
| `middleware/` | Auth, authorization, rate limiting, cross-cutting logic |
|
||||
| `common/` | Configuration, global state, initialization |
|
||||
| `utils/` | Pure helpers |
|
||||
|
||||
## Frontend Layers
|
||||
|
||||
| Directory | Responsibility |
|
||||
| --- | --- |
|
||||
| `app/` | Routes, layouts, page composition |
|
||||
| `features/` | Business-domain modules |
|
||||
| `components/` | Cross-feature reusable components |
|
||||
| `lib/` | API client, env, utilities, constants |
|
||||
| `store/` | Small cross-page UI state |
|
||||
| `types/` | Shared types |
|
||||
@@ -1,79 +0,0 @@
|
||||
# 接入 Agent
|
||||
|
||||
OpenFlare Agent 运行在节点侧,负责注册、心跳、同步配置、写入 OpenResty 文件、校验、reload、失败回滚与自更新。
|
||||
|
||||
## 接入方式
|
||||
|
||||
Agent 支持两种认证入口:
|
||||
|
||||
| 方式 | 适用场景 |
|
||||
| --- | --- |
|
||||
| `agent_token` | 已在管理端创建或分配节点,使用节点专属凭证接入 |
|
||||
| `discovery_token` | 首次自动注册节点,由 Server 置换为节点专属凭证 |
|
||||
|
||||
二者至少填写一个。
|
||||
|
||||
## 安装脚本
|
||||
|
||||
使用 `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
|
||||
```
|
||||
|
||||
使用节点专属 `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
|
||||
```
|
||||
|
||||
安装脚本会写入 `/opt/openflare-agent`,创建 `openflare-agent.service`,并可重复执行以重装或升级 Agent。
|
||||
|
||||
## 配置文件示例
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_container_name": "openflare-openresty",
|
||||
"openresty_docker_image": "openresty/openresty:alpine",
|
||||
"openresty_observability_port": 18081,
|
||||
"observability_replay_minutes": 15,
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
未配置 `openresty_path` 时,Agent 默认使用 Docker OpenResty。裸 OpenResty 模式需要显式配置本机路径和必要的配置写入目录。
|
||||
|
||||
## 源码运行
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
export LOG_LEVEL='info'
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
## 编译后二进制运行
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
## 卸载
|
||||
|
||||
如需彻底卸载 Agent 并清空本地数据:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
卸载脚本会停止并移除 `openflare-agent.service`,删除 `/opt/openflare-agent`,并根据配置尝试清理 Docker OpenResty 容器。
|
||||
@@ -0,0 +1,29 @@
|
||||
# 引用与致谢
|
||||
|
||||
OpenFlare 本质上是一个方案整合项目, 在设计与实现过程中借鉴了众多开源项目的优秀理念、架构设计和技术实现。以下是 OpenFlare 在核心底层引擎、安全防护机制以及前后端系统框架等方面所引用的关键开源项目,以及对这些项目及其社区的感谢。
|
||||
|
||||
---
|
||||
|
||||
### 1. OpenResty
|
||||
* **项目定位**:基于 Nginx 与 Lua 的高性能 Web 平台。
|
||||
* **在 OpenFlare 中的作用**:作为全局数据面(Data Plane)的边缘网关。所有的公网 Web 流量均首先由 OpenResty 接收,在此处进行高并发的 HTTPS 握手、WAF 安全规则比对、防 CC 人机验证,并最终执行反向代理转发。
|
||||
* **项目链接**:[OpenResty 官网](https://openresty.org/)
|
||||
|
||||
### 2. FRP (Fast Reverse Proxy)
|
||||
* **项目定位**:高性能的反向代理应用,专注于内网穿透。
|
||||
* **在 OpenFlare 中的作用**:作为内网穿透子系统的底层隧道引擎。中继端管理器 `openflare-relay` 负责守护和调度 `frps` 引擎,而内网客户端 `openflared` 则负责在本地自动生成 TOML 配置并守护多路复用 `frpc` 子进程。
|
||||
* **项目链接**:[fatedier/frp (GitHub)](https://github.com/fatedier/frp)
|
||||
|
||||
---
|
||||
|
||||
### 3. Anubis (PoW 方案)
|
||||
* **项目定位**:基于工作量证明(Proof of Work)的轻量级人机验证防护方案。
|
||||
* **在 OpenFlare 中的作用**:为网关 WAF 提供了核心的**无感防 CC 人机挑战**能力。
|
||||
|
||||
---
|
||||
|
||||
### 4. gin-template
|
||||
* **项目定位**:基于 Go Gin 与前端构建的现代化全栈开发脚手架模板。
|
||||
* **在 OpenFlare 中的作用**:为 OpenFlare 控制面(Server)提供了规范、统一的前后端系统架构雏形。
|
||||
|
||||
---
|
||||
@@ -1,262 +0,0 @@
|
||||
# 部署说明
|
||||
|
||||
本文档说明 OpenFlare `1.0.0` 之后的部署基线、联调入口、升级方式与 Agent 一键部署流程。
|
||||
|
||||
## 前置条件
|
||||
|
||||
Server:
|
||||
|
||||
* Go 1.25+
|
||||
* Node.js 18+
|
||||
* 可写 SQLite 文件目录,或可访问的 PostgreSQL 实例
|
||||
|
||||
Agent:
|
||||
|
||||
* Go 1.25+
|
||||
* 对 Agent 数据目录有写权限
|
||||
* 本机模式下可执行 `openresty -t` 与 `openresty -s reload`
|
||||
* Docker 模式下具备 Docker 执行权限
|
||||
|
||||
## Docker Compose 启动 Server
|
||||
|
||||
推荐生产部署使用 PostgreSQL:
|
||||
|
||||
```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
|
||||
container_name: openflare
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
SESSION_SECRET: replace-with-random-string
|
||||
SQLITE_PATH: /data/openflare.db
|
||||
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:
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
首次访问 `http://localhost:3000`,默认账号为 `root` / `123456`。登录后请立即修改默认密码。
|
||||
|
||||
## 源码启动 Server
|
||||
|
||||
先构建管理端前端:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
再启动 Server:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
export SESSION_SECRET='replace-with-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
# 可选:设置后优先使用 PostgreSQL。
|
||||
# 如果 PostgreSQL 为空且本地 SQLite 文件存在,启动时会自动迁移数据。
|
||||
# export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
go run .
|
||||
```
|
||||
|
||||
默认监听 `3000` 端口。
|
||||
|
||||
## Swagger
|
||||
|
||||
登录管理端后访问:
|
||||
|
||||
```text
|
||||
http://localhost:3000/swagger/index.html
|
||||
```
|
||||
|
||||
本地重新生成 Swagger:
|
||||
|
||||
```bash
|
||||
go install github.com/swaggo/swag/cmd/swag@v1.16.4
|
||||
cd openflare_server
|
||||
swag init -g main.go -o docs
|
||||
```
|
||||
|
||||
## Agent 接入模式
|
||||
|
||||
Agent 支持两种接入模式。
|
||||
|
||||
使用节点专属 `agent_token`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_container_name": "openflare-openresty",
|
||||
"openresty_docker_image": "openresty/openresty:alpine",
|
||||
"openresty_observability_port": 18081,
|
||||
"observability_replay_minutes": 15,
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
使用全局 `discovery_token`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"discovery_token": "replace-with-global-discovery-token",
|
||||
"data_dir": "./data",
|
||||
"openresty_container_name": "openflare-openresty",
|
||||
"openresty_docker_image": "openresty/openresty:alpine",
|
||||
"openresty_observability_port": 18081,
|
||||
"observability_replay_minutes": 15,
|
||||
"heartbeat_interval": 10000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
* `agent_token` 与 `discovery_token` 至少填写一个。
|
||||
* 未配置 `openresty_path` 时默认使用 Docker OpenResty。
|
||||
* Agent 会暴露本机观测端口并在 Server 恢复后补传最近窗口数据。
|
||||
|
||||
## 一键部署 Agent
|
||||
|
||||
使用 `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
|
||||
```
|
||||
|
||||
使用节点专属 `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
|
||||
```
|
||||
|
||||
支持参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `--server-url` | Server 地址 |
|
||||
| `--discovery-token` | 首次自动注册 Token |
|
||||
| `--agent-token` | 节点专属 Token |
|
||||
| `--install-dir` | 安装目录 |
|
||||
| `--repo` | 下载 Agent 的仓库 |
|
||||
| `--no-service` | 不创建系统服务 |
|
||||
|
||||
安装脚本会下载最新 Agent、生成 `agent.json`、创建 `openflare-agent.service` 并启动服务。
|
||||
|
||||
## 手动启动 Agent
|
||||
|
||||
源码运行:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
export LOG_LEVEL='info'
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
编译后二进制运行:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
export LOG_LEVEL='info'
|
||||
./openflare-agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
## 卸载 Agent
|
||||
|
||||
如需彻底卸载 Agent 并清空本地数据:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/uninstall-agent.sh | bash
|
||||
```
|
||||
|
||||
支持参数:
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `--install-dir` | Agent 安装目录 |
|
||||
| `--service-name` | systemd 服务名 |
|
||||
|
||||
卸载脚本会先停止 Agent、移除 `openflare-agent.service`、删除整个安装目录,再根据卸载前保存的 `agent.json` 判断 OpenResty 安装方式:
|
||||
|
||||
* Docker 模式:删除对应容器,并尝试移除 OpenResty 镜像。
|
||||
* 本机 `openresty_path` 模式:不改动本机 OpenResty,仅提示用户手动卸载。
|
||||
|
||||
## 最小联调步骤
|
||||
|
||||
1. 在管理端准备 `agent_token` 或 `discovery_token`。
|
||||
2. 启动 Agent 并确认节点上线。
|
||||
3. 新增一条启用中的反代规则。
|
||||
4. 生成并激活新版本。
|
||||
5. 确认 Agent 拉取配置、执行 `openresty -t`、reload 并上报结果。
|
||||
|
||||
预期管理端可看到节点在线状态、节点当前版本、最近一次应用结果,以及自动注册后的专属 `agent_token`。
|
||||
|
||||
## 升级说明
|
||||
|
||||
* Root 用户可在管理端顶栏检查并升级 Server 正式版。
|
||||
* 如需尝试 preview 版本,可手动检查对应发布。
|
||||
* 节点 Agent 默认只跟随正式版自动更新;preview 升级需要手动触发。
|
||||
* 也可通过上传 Server 二进制的方式执行确认升级。
|
||||
|
||||
## 常用验证命令
|
||||
|
||||
Server:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
Agent:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
Frontend:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
pnpm build
|
||||
```
|
||||
+51
-27
@@ -1,45 +1,69 @@
|
||||
# 发布第一份配置
|
||||
|
||||
OpenFlare 的发布链路以完整配置版本为中心。你修改网站配置后,需要生成新版本并激活,Agent 才会在后续 heartbeat 中拉取并应用。
|
||||
你会学到:如何以最简单的方式创建第一条反向代理规则、发布配置版本,并确认 Agent 已经拉取并应用配置。
|
||||
|
||||
## 创建网站配置
|
||||
OpenFlare 的发布链路以“不可变配置版本”为核心。你在管理端修改规则后,需要发布并激活新版本,在线的 Agent 才会自动同步并应用。
|
||||
|
||||
在管理端新增网站配置时至少需要:
|
||||
---
|
||||
|
||||
| 字段 | 说明 |
|
||||
## 发布前检查
|
||||
|
||||
在开始发布前,请确保以下条件已满足:
|
||||
|
||||
| 检查项 | 状态要求 |
|
||||
| --- | --- |
|
||||
| 网站名称 | 业务唯一标识;未显式填写时默认使用主域名 |
|
||||
| 域名 | 至少一个域名,第一项视为主域名 |
|
||||
| 源站地址 | 合法的 `http://` 或 `https://` 上游地址 |
|
||||
| 启用状态 | 只有启用的网站配置会参与发布渲染 |
|
||||
| **Server** | 控制面板已正常启动,且能顺利登录管理端 |
|
||||
| **Agent** | 至少有一个 Agent 节点处于在线状态(可在「节点管理」中确认) |
|
||||
| **源站** | 确认你的后端源站服务可从 Agent 宿主机正常访问 |
|
||||
| **域名/测试** | 域名已完成 DNS 解析,或者准备好在客户端使用本地 hosts / curl 命令行 Host 头进行测试 |
|
||||
|
||||
同一个域名只能属于一个网站配置。同一网站内的流量限制、反向代理和缓存配置按站点共享。
|
||||
---
|
||||
|
||||
## 绑定证书
|
||||
## 步骤一:创建首个网站配置
|
||||
|
||||
HTTPS 证书按域名绑定。没有绑定证书的域名不会被自动放入 `443 ssl` server 块。
|
||||
为了快速验证,我们首先部署一个最基础的 HTTP 反代站点:
|
||||
|
||||
如果一个网站包含多个域名,发布渲染会按证书分组生成 HTTPS 配置,并确保所有域名仍属于同一站点快照。
|
||||
1. 登录控制面板,进入 **「网站配置」**,点击 **「创建网站」**。
|
||||
2. 填写最基础的站点配置:
|
||||
* **网站名称**:输入简易标识(如 `first-app`)。
|
||||
* **域名 (Domains)**:输入用于测试的域名(如 `first.example.com`)。**第一项默认作为主域名**。
|
||||
3. 配置上游源站(Upstream):
|
||||
* **源站类型**:选择「标准反代」。
|
||||
* **源站地址**:勾选手动输入并填入后端服务地址(如 `http://10.0.0.10:8080` 或测试专用的 `http://httpbin.org`)。
|
||||
4. 点击保存,完成网站创建。
|
||||
|
||||
## 发布与激活
|
||||
> [!TIP]
|
||||
> **关于 HTTPS 与证书准备**
|
||||
> 本节仅引导快速部署基础 HTTP 规则。若你需要导入已有的 SSL 证书或通过 ACME 协议向 Let's Encrypt 自动申请证书并开启 443 端口 HTTPS 代理,请前往 [新建反代配置](./proxy-config.md) 查阅详细步骤。
|
||||
|
||||
标准链路:
|
||||
---
|
||||
|
||||
```text
|
||||
修改规则 -> 预览/查看 diff -> 发布 -> 生成完整配置版本 -> 激活版本 -> Agent 拉取 -> 本地应用 -> 上报结果
|
||||
```
|
||||
## 步骤二:预览并发布配置版本
|
||||
|
||||
发布时 Server 会读取全部启用的网站配置、OpenResty 主配置模板、性能参数与缓存参数,渲染完整 OpenResty 配置,计算 `checksum`,写入 `config_versions`,再切换激活版本。
|
||||
新增的网站配置仍保存在 Server 的数据库中,处于草稿状态,需要通过发布版本分发到数据面:
|
||||
|
||||
## 验证结果
|
||||
1. 点击控制面板右上角的 **「配置预览」** 按钮,系统会展示本次新增路由的物理配置文件 Diff 差异。
|
||||
2. 确认渲染出的配置内容正确无误后,点击 **「发布并激活」**。
|
||||
3. 控制面将生成一个唯一的配置版本号(格式为 `YYYYMMDD-NNN`)。
|
||||
|
||||
发布后在管理端确认:
|
||||
---
|
||||
|
||||
| 位置 | 期望结果 |
|
||||
| --- | --- |
|
||||
| 节点列表 | 节点在线 |
|
||||
| 节点详情 | 当前版本与激活版本一致 |
|
||||
| 应用记录 | 最近一次应用成功 |
|
||||
| 版本页面 | 新版本处于激活状态 |
|
||||
## 步骤三:验证 Agent 生效状态
|
||||
|
||||
如果目标版本应用失败并回滚,Agent 会在本地阻断同一 `version + checksum` 的重复应用,直到控制面激活版本或 checksum 发生变化。
|
||||
发布成功后,控制面会立即通过 WebSocket 通知在线 Agent(若 WebSocket 离线,则会在 Agent 的心跳中作为差分感知):
|
||||
|
||||
1. **管理端验证**:进入「节点管理」-> 点击节点进入详情,检查**当前版本号**是否已成功变为刚刚发布的最新激活版本,且「应用记录」显示为成功。
|
||||
2. **边缘节点验证**:你可以在 Agent 节点宿主机上通过日志检查应用情况:
|
||||
```bash
|
||||
# 如果是 Docker 部署的 Agent
|
||||
docker logs openflare-agent
|
||||
|
||||
# 如果是本地 systemd 部署的 Agent
|
||||
journalctl -u openflare-agent -n 50 --no-pager
|
||||
```
|
||||
3. **连通性测试**:
|
||||
在客户端电脑上,使用 `curl` 携带测试 Host 请求 Agent 节点的 IP 地址进行最终验证:
|
||||
```bash
|
||||
curl -I -H "Host: first.example.com" http://AGENT_NODE_IP
|
||||
```
|
||||
若返回的状态码与后端源站响应一致,即代表你的第一条反代规则已成功在边缘节点落地生效!
|
||||
|
||||
+44
-10
@@ -1,15 +1,49 @@
|
||||
# 指南
|
||||
|
||||
本部分面向使用者和部署者,帮助你把 OpenFlare 从首次启动推进到第一份可运行的代理配置。
|
||||
你会学到:OpenFlare 文档如何组织、首次运行应该读哪些页面,以及部署、使用、排查和开发分别从哪里开始。
|
||||
|
||||
推荐阅读顺序:
|
||||
OpenFlare 是一套自托管的 OpenResty 控制面。它把反向代理网站配置、配置版本发布、Agent 节点同步、TLS 证书和基础观测放到一个管理端中,适合单团队或单组织管理多台代理节点。
|
||||
|
||||
1. [快速开始](./quick-start.md):用 Docker Compose 启动 Server,并完成首次登录。
|
||||
2. [部署说明](./deployment.md):查看生产部署、Agent 一键安装、联调与升级。
|
||||
3. [SSO 登录配置](./sso.md):配置 GitHub OAuth 或标准 OIDC 登录入口。
|
||||
4. [启动 Server](./server.md):了解源码启动、前端构建和 Swagger 入口。
|
||||
5. [接入 Agent](./agent.md):选择 `agent_token` 或 `discovery_token`,让节点上线。
|
||||
6. [发布第一份配置](./first-site.md):创建网站配置,发布并确认节点应用。
|
||||
7. [升级与维护](./upgrade.md):了解升级、卸载、验证和日常维护入口。
|
||||
## 推荐阅读路径
|
||||
|
||||
如果你要参与开发,先阅读 [设计](../design/) 与 [开发约束](../design/development.md),再进入代码修改。
|
||||
如果你第一次接触 OpenFlare,按下面顺序阅读:
|
||||
|
||||
1. [快速开始](./quick-start.md):用 Docker Compose 启动 Server,登录管理端,并接入第一个 Agent。
|
||||
2. [发布第一份配置](./first-site.md):快速新建一条最基础的 HTTP 反代站点规则,并验证节点生效状态。
|
||||
3. [新建反代配置](./proxy-config.md):一步一步了解如何从证书导入与申请开始,配置 HTTPS 加密与上游源站管理。
|
||||
4. [Pages 静态托管使用](./pages-usage.md):了解静态项目 ZIP 上传限制、SPA Fallback、以及内置 API 反向代理配置。
|
||||
5. [内网穿透与隧道使用](./tunnel-usage.md):部署 Relay 与 Client,实现安全、无公网 IP 反向穿透。
|
||||
6. [WAF 安全防护使用](./waf-usage.md):配置 WAF 规则组,掌握 IP 黑白名单、自动/订阅 IP 组、地域限制与 PoW CC 防护。
|
||||
7. [WAF 自动 IP 组语法](./waf-ip-group-expr.md):编写自动 IP 组 Expr 规则,了解关键字含义和预设规则。
|
||||
8. [Uptime Kuma 监控同步](./uptime-kuma.md):配置并使用 Uptime Kuma 自动差分同步和监控范围控制。
|
||||
9. [SSO 登录配置](./sso.md):配置 GitHub 或 OIDC 实现第三方单点登录 (SSO) 接入。
|
||||
10. [故障排查](./troubleshooting.md):按症状排查登录、数据库、节点同步、OpenResty 应用和前端构建问题。
|
||||
11. [引用与致谢](./credits.md):查看系统依赖的优秀开源项目与社区致谢清单。
|
||||
|
||||
## 按角色查找
|
||||
|
||||
| 你想做什么 | 推荐入口 |
|
||||
| --- | --- |
|
||||
| 5 分钟内跑起管理端 | [快速开始](./quick-start.md) |
|
||||
| 发布第一条反向代理配置 | [发布第一份配置](./first-site.md) |
|
||||
| 配置域名证书与高级反代 | [新建反代配置](./proxy-config.md) |
|
||||
| 托管单页应用或静态网站 | [Pages 静态托管使用](./pages-usage.md) |
|
||||
| 配置内网穿透映射 | [内网穿透与隧道使用](./tunnel-usage.md) |
|
||||
| 配置防 CC 与 IP 组拦截 | [WAF 安全防护使用](./waf-usage.md) |
|
||||
| 编写自动 IP 组规则 | [WAF 自动 IP 组语法](./waf-ip-group-expr.md) |
|
||||
| 自动同步监测站点状态 | [Uptime Kuma 监控同步](./uptime-kuma.md) |
|
||||
| 接入或重装节点 Agent | [接入 Agent](../deployment/agent.md) |
|
||||
| 从源码启动 Server | [启动 Server](../deployment/server.md) |
|
||||
| 配置 GitHub 或 OIDC 登录 | [SSO 登录配置](./sso.md) |
|
||||
| 升级 Server 或 Agent | [升级与维护](../deployment/upgrade.md) |
|
||||
| 参与开发或修复问题 | [启动 Server](../deployment/server.md) 与 [开发约束](../guideline/Constraints.md) |
|
||||
| 理解架构和发布模型 | [系统架构](../design/architecture.md) 与 [Agent 与发布模型](../design/agent-design.md) |
|
||||
| 查看开源引用与致谢 | [引用与致谢](./credits.md) |
|
||||
|
||||
## 文档分区
|
||||
|
||||
`guide/` 面向使用者和部署者,提供从安装到日常操作的可执行步骤。
|
||||
|
||||
`reference/` 收敛稳定事实,例如配置字段、命令、API 响应约定和仓库结构。
|
||||
|
||||
`design/` 面向维护者和贡献者,描述产品边界、系统架构、Agent 与发布模型和工程约束。新增能力或改变边界前,应先更新对应设计文档。
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# Pages 静态托管使用
|
||||
|
||||
你会学到:如何在 OpenFlare 中使用 Pages 静态托管功能部署前端项目(如 React、Vue 等 SPA 或 VitePress、Hugo 等静态站点),配置单页应用 (SPA) Fallback 路由以及接口反向代理 (API Proxy),并理解不可变部署与 Agent 侧原子切换的底层逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 核心机制与工作流
|
||||
|
||||
OpenFlare Pages 提供受 Cloudflare Pages 启发的 **Direct Upload (直接上传)** 静态网站托管服务。它与常规代理站点的不同之处在于,数据面的边缘节点 (Agent) 会将静态文件拉取并解压到节点本地,直接通过本地的 OpenResty 提供高性能的静态文件服务,无需维护额外的 Nginx 宿主机静态目录同步。
|
||||
|
||||
```text
|
||||
[ 管理员 / CI ] ────── 1. 上传 ZIP 压缩包 ──────► [ OpenFlare Server ]
|
||||
│
|
||||
[ 访客浏览器 ] ◄────── 4. 访问页面 / 静态资源 ────────── [ Agent 节点 / OpenResty ]
|
||||
▲
|
||||
│
|
||||
2. 检查 Checksum 并拉取 ZIP
|
||||
3. 解压并原子切换 current 链接
|
||||
```
|
||||
|
||||
1. **直接上传部署包**:在控制面上传预构建好的网站 `.zip` 压缩包,Server 会生成一条带有唯一 SHA-256 校验和 (Checksum) 的不可变部署记录。
|
||||
2. **发布与推送**:在路由配置中将源站类型 (Upstream Type) 设为 `Pages 静态托管` 并绑定项目。发布配置版本后,Server 会广播给所有 Agent 节点。
|
||||
3. **安全拉取与部署**:Agent 节点识别到新配置引用了新的 Pages 部署,增量下载 ZIP 包,校验 Checksum 保证一致性,并在本地解压、完成原子目录切换,重载 OpenResty 使服务生效。
|
||||
|
||||
---
|
||||
|
||||
## 第一步:上传部署包与创建 Pages 项目
|
||||
|
||||
1. 登录管理端控制面板,进入左侧导航 **「静态托管 (Pages)」**,点击 **「创建项目」**。
|
||||
2. 填写项目基本信息:
|
||||
* **项目名称**:业务名称(如 `我的前端应用`)。
|
||||
* **项目标识 (Slug)**:URL 友好的唯一英文标识(如 `my-react-app`),将作为存储目录的文件夹名。
|
||||
3. 设定站点目录结构与入口:
|
||||
* **入口文件名**:默认为 `index.html`。
|
||||
* **静态资源根路径 (RootDir)**:如果你的打包产物在压缩包的子目录下(例如打包出来的 zip 里包含一个 `dist/` 目录),则需要在这里填入子路径(如 `dist`)。若打包产物直接在 zip 根目录,留空即可。
|
||||
4. **上传 ZIP 压缩包**:
|
||||
* 上传你的项目静态资源打包生成的 `.zip` 文件。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **部署包安全限制规范**
|
||||
> 为了保障控制面和边缘节点的系统安全与性能,上传的部署包必须满足以下硬性指标,否则会被系统拒绝:
|
||||
> * **大小限制**:ZIP 压缩包体积不得超过 **25 MiB**,解压后的总文件大小不得超过 **100 MiB**。
|
||||
> * **数量限制**:解压后的文件总数不得超过 **1,000 个**。
|
||||
> * **软链接拦截**:ZIP 包内禁止包含任何软链接 (Symbolic Link),防御软链接劫持攻击。
|
||||
> * **Zip-Slip 防御**:压缩包中所有文件路径会被强制规范化,禁止使用 `..` 或以 `/` 开头,防止解压路径穿越攻击。
|
||||
> * **入口文件检查**:你指定的入口文件(在静态资源根路径下,如 `dist/index.html`)**必须在压缩包中存在**。
|
||||
|
||||
---
|
||||
|
||||
## 第二步:配置高级路由规则
|
||||
|
||||
在项目详情的配置页面中,你可以根据前端项目类型开启以下高级特性:
|
||||
|
||||
### 1. 单页应用 (SPA) Fallback 路由
|
||||
对于使用 React Router、Vue Router 等进行前端路由的单页应用 (SPA),当用户直接刷新类似 `/profile/settings` 的子路径时,边缘节点本地并不存在该物理文件,会导致 404 错误。
|
||||
* **配置方式**:在项目设置中开启 **「SPA Fallback」**,并将路径设为入口文件(如 `/index.html`)。
|
||||
* **生效逻辑**:开启后,如果访客请求的静态资源在物理上不存在,OpenResty 会自动降级重定向渲染入口文件,将路由交由前端 JavaScript 接管,避免 404 报错。
|
||||
|
||||
### 2. 内置 API 反向代理
|
||||
为了避免前端请求后端 API 时遭遇跨域 (CORS) 限制,Pages 托管支持在同一个域名下直通后端 API。
|
||||
* **配置方式**:
|
||||
* **API 代理路径 (APIProxyPath)**:匹配的 URL 前缀(如 `/api`)。
|
||||
* **后端服务地址 (APIProxyPass)**:后端 API 的源站地址(如 `http://10.0.0.5:8080`)。
|
||||
* **重写规则 (APIProxyRewrite)**:可选。如果需要剥离前缀或重写路径,可使用正则匹配。例如:
|
||||
* 剥离前缀:将请求 `/api/users` 重写为 `/users` 发送给后端,配置为 `^/api/(.*)$ /$1`。
|
||||
* **生效逻辑**:所有以 `/api` 开头的请求会被直接转发至后端服务,而其他请求则继续由静态托管服务处理。
|
||||
|
||||
---
|
||||
|
||||
## 第三步:绑定代理路由并发布
|
||||
|
||||
Pages 项目配置并上传好部署包后,需要绑定到对外公开的域名上才能被访客访问。
|
||||
|
||||
1. 导航至左侧菜单 **「网站配置」**,创建或编辑一个代理站点。
|
||||
2. 在「路由规则」中修改或添加一条路由:
|
||||
* **源站类型 (Upstream Type)**:选择 **「Pages 静态托管」**。
|
||||
* **绑定 Pages 项目**:选择你刚才创建的项目,并指定要激活的部署版本(默认会自动关联最新上传成功的部署)。
|
||||
3. 点击右上角 **「配置预览」** -> 确认无误后点击 **「发布并激活」**。
|
||||
|
||||
## 运维与回滚
|
||||
|
||||
* **不可变部署与回滚**:每次在 Pages 项目下上传 `.zip` 文件,系统都会产生一个全新且唯一的部署版本。如果在历史部署列表中将上一版本设为激活并重新发布,可实现边缘节点的秒级回滚。
|
||||
* **原子切换与自愈**:边缘节点(Agent)在拉取静态资源包时,会执行校验与流式解压,并通过原子切换物理目录来保障服务的无缝过渡。同时,Agent 会定时清理不再引用的历史部署包。
|
||||
|
||||
> [!TIP]
|
||||
> 关于不可变部署、目录结构设计、增量拉取和安全防逃逸校验等底层架构与自愈细节,请参阅 [Pages 静态托管设计](../design/pages-design.md)。
|
||||
@@ -0,0 +1,102 @@
|
||||
# 新建反代配置
|
||||
|
||||
你会学到:如何一步一步在 OpenFlare 中从零新建并发布一个反向代理网站配置。本指南将指导你如何完成证书导入与申请、源站定义、路由规则配置、版本发布以及连通性验证。
|
||||
|
||||
---
|
||||
|
||||
## 推荐操作流程
|
||||
|
||||
在网关控制面中,建议遵循以下步骤新增反代规则:
|
||||
|
||||
```text
|
||||
[ 步骤 1. 证书管理 ] ──► [ 步骤 2. 源站定义 (可选) ] ──► [ 步骤 3. 新增网站配置 ]
|
||||
│
|
||||
[ 步骤 5. 验证访问 ] ◄── [ 步骤 4. 发布与激活版本 ] ◄───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第一步:证书准备(导入与申请)
|
||||
|
||||
在使用 HTTPS 安全加密流量前,你需要先配置对应的 TLS 证书。OpenFlare 支持以下两种证书获取方式:
|
||||
|
||||
### 1. 手动导入已有证书
|
||||
如果你已经有第三方的证书(如腾讯云、阿里云申请的免费/收费证书,或者自签证书):
|
||||
1. 导航至左侧菜单 **「证书管理」**,点击 **「导入证书」**。
|
||||
2. 填入证书名称(如 `my-domain-cert`)。
|
||||
3. 复制并粘贴你的 **证书内容 (PEM 格式公钥)** 以及 **证书私钥 (KEY 格式)**,点击保存。
|
||||
|
||||
### 2. 通过 ACME 协议自动申请
|
||||
OpenFlare 集成了 ACME 客户端,支持自动向 Let's Encrypt 申请并到期续签证书:
|
||||
1. **添加 ACME 账户**:进入「证书管理」->「ACME 账户」->「创建账户」,填入你的联系邮箱。
|
||||
2. **添加 DNS 账户 (用于 DNS-01 验证)**:进入「证书管理」->「DNS 账户」->「创建账户」,选择你的 DNS 托管商(当前仅 Cloudflare)并填入 API Token 凭证。
|
||||
3. **申请证书**:在「证书管理」中点击「申请证书」:
|
||||
* 选择配置好的 ACME 账户和 DNS 账户。
|
||||
* 输入需要托管证书的域名(支持通配符,如 `*.example.com`)。
|
||||
* 点击申请,系统将自动配置 DNS 挑战码并向 CA 申请证书,且会在到期前 30 天自动触发续期。
|
||||
|
||||
---
|
||||
|
||||
## 第二步:准备上游源站(可选)
|
||||
|
||||
源站(Origin)代表被代理的后端真实服务地址。虽然在新建网站时可以直接填写 IP,但推荐先在源站库中进行注册,以便后续复用与维护:
|
||||
|
||||
1. 进入左侧导航 **「源站管理」**,点击 **「创建源站」**。
|
||||
2. 填写源站名称(如 `production-api`)。
|
||||
3. 填入合法的上游地址(如 `http://10.0.0.10:8080`),点击保存。
|
||||
|
||||
---
|
||||
|
||||
## 第三步:新建网站配置
|
||||
|
||||
证书和源站就绪后,即可创建核心网站代理路由:
|
||||
|
||||
1. 进入左侧导航 **「网站配置」**,点击 **「创建网站」**。
|
||||
2. 填写网站基本配置:
|
||||
* **网站名称**:业务唯一标识(如 `app-portal`)。
|
||||
* **域名 (Domains)**:输入该站点绑定的域名列表。**第一项将自动视为主域名**。
|
||||
3. 配置上游源站(Upstream):
|
||||
* **源站类型**:选择「标准反代」。
|
||||
* **源站地址**:从下拉框中选择第二步创建的源站;或者勾选手动输入并填入 `http://10.0.0.20:9000`。
|
||||
4. **绑定证书启用 HTTPS**:
|
||||
* 在域名列表中,点击域名旁边的配置按钮或 HTTPS 切换开关。
|
||||
* 勾选「启用 HTTPS」,并从证书下拉列表中选择第一步准备好的证书。
|
||||
* *注意:未绑定证书的域名只会保留 80 端口 HTTP 服务,不会被写入 443 端口代理中。*
|
||||
5. 点击保存创建配置。
|
||||
|
||||
---
|
||||
|
||||
## 第四步:发布并生效配置
|
||||
|
||||
你在管理端新增的网站配置仅保存在 Server 数据库中,**不会立即生效**。必须生成配置版本快照并分发到 Agent 边缘节点:
|
||||
|
||||
1. 点击控制面板右上角的 **「配置预览」** 按钮。
|
||||
2. 检查配置文件的 Diff 差异,确认你刚刚新增的 `server` 块以及证书绑定规则无误。
|
||||
3. 点击 **「发布并激活」** 按钮。
|
||||
4. **Agent 落地机制**:
|
||||
* 数据面的 Agent 节点在心跳中发现激活的版本 Checksum 变更,会自动拉取完整的 OpenResty 配置文件和证书包到本地。
|
||||
* 自动在本地执行配置校验(类似于 `openresty -t`),确认无语法错误后,执行平滑重载(`reload`)。
|
||||
* *如果重载或校验失败,Agent 会安全阻断并回滚至上一稳定版本,保证节点高可用。*
|
||||
|
||||
---
|
||||
|
||||
## 第五步:连通性与回滚验证
|
||||
|
||||
### 1. 验证访问
|
||||
你可以通过以下方式验证新配置是否生效:
|
||||
* **浏览器访问**:直接在浏览器输入 `https://your-domain.com` 查看是否成功代理后端。
|
||||
* **命令行验证**(推荐):使用 `curl` 探测:
|
||||
```bash
|
||||
curl -I https://your-domain.com
|
||||
```
|
||||
* **绕过 DNS 校验**:若你的域名尚未正式解析,可以临时指定 `Host` 请求头请求 Agent 节点物理 IP:
|
||||
```bash
|
||||
curl -I -H "Host: your-domain.com" https://AGENT_NODE_IP --insecure
|
||||
```
|
||||
|
||||
### 2. 一键秒级回滚
|
||||
如果发布的新配置导致了线上业务异常:
|
||||
1. 导航至左侧 **「配置版本」** 菜单。
|
||||
2. 在历史列表中找到发布前的上一个稳定版本。
|
||||
3. 点击 **「激活此版本」**。
|
||||
4. 所有在线 Agent 节点将在秒级自动重载回历史配置,实现秒级避险。
|
||||
+129
-15
@@ -1,10 +1,32 @@
|
||||
# 快速开始
|
||||
|
||||
OpenFlare 的最小运行单元包含一个 Server 和至少一个 Agent。Server 负责管理端、配置版本与节点状态,Agent 运行在代理节点上,负责写入 OpenResty 配置并 reload。
|
||||
你会学到:如何用 Docker Compose 启动 OpenFlare Server、完成首次登录、接入第一个 Agent,并验证一份配置是否已经发布到节点。
|
||||
|
||||
## 启动 Server
|
||||
OpenFlare 的最小运行单元包含:
|
||||
|
||||
推荐使用 PostgreSQL 与 Docker Compose:
|
||||
| 组件 | 职责 |
|
||||
| --- | --- |
|
||||
| Server | 管理端 UI、管理 API、Agent API、配置渲染、版本发布与状态存储 |
|
||||
| Agent | 运行在代理节点上,拉取配置、写入 OpenResty、执行校验与 reload |
|
||||
| OpenResty | 实际接收流量并反向代理到源站 |
|
||||
|
||||
Agent 统一通过 OpenResty 二进制控制运行时。本地部署需要节点上已有 `openresty` 可执行文件;Docker 部署可直接运行内置 OpenResty 的 Agent 镜像。
|
||||
|
||||
## 环境要求
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- | --- |
|
||||
| Docker / Docker Compose | 用于启动 Server 和 PostgreSQL;如果采用 Docker Agent 镜像,也用于运行 Agent |
|
||||
| OpenResty | 本地安装 Agent 时需要可执行 `openresty`,或在安装脚本中指定路径 |
|
||||
| 可访问端口 | Server 默认监听 `3000`,Agent 节点需要能访问 Server 地址 |
|
||||
| 浏览器 | 用于访问管理端 |
|
||||
|
||||
- **Docker**:`20.10.0+`
|
||||
- **Docker Compose**:`2.0.0+`
|
||||
|
||||
## 1. 启动 Server
|
||||
|
||||
在空目录中创建 `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -32,20 +54,36 @@ services:
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
SESSION_SECRET: replace-with-random-string
|
||||
JWT_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:
|
||||
```
|
||||
|
||||
启动服务:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
访问 `http://localhost:3000`。
|
||||
确认容器已经运行:
|
||||
|
||||
```bash
|
||||
docker compose ps
|
||||
docker compose logs -f openflare
|
||||
```
|
||||
|
||||
看到 `server listening` 且 `openflare` 容器状态为 running 后,访问:
|
||||
|
||||
```text
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
默认账号:
|
||||
|
||||
@@ -53,11 +91,44 @@ docker compose up -d
|
||||
| --- | --- |
|
||||
| `root` | `123456` |
|
||||
|
||||
首次登录后请立即修改默认密码,并按需关闭新用户注册。
|
||||
首次登录后请立即修改默认密码。
|
||||
|
||||
## 接入第一个节点
|
||||
## 2. 准备 Agent Token
|
||||
|
||||
在管理端准备 `discovery_token` 或节点专属 `agent_token`,然后在节点上执行安装脚本。
|
||||
Agent 可以用两类凭证接入:
|
||||
|
||||
| 凭证 | 适用场景 |
|
||||
| --- | --- |
|
||||
| `discovery_token` | 首次自动注册节点,由 Server 换成节点专属 Token |
|
||||
| `agent_token` | 已经在管理端创建或分配节点,直接使用节点专属 Token |
|
||||
|
||||
在管理端准备其中一种凭证后,进入下一步。
|
||||
|
||||
- **`discovery_token`** 获取菜单路径:「系统设置」->「自动注册」
|
||||
- **`agent_token`** 获取菜单路径:「节点管理」->「新增节点」
|
||||
|
||||
## 3. 安装/运行 Agent
|
||||
|
||||
Agent 部署方式推荐使用 Docker 部署(即直接运行内置 OpenResty 的 Agent 镜像);亦支持通过安装脚本将 Agent 部署在本地宿主机上。
|
||||
|
||||
### 方式 A:Docker 运行 Agent(推荐)
|
||||
|
||||
在代理节点上直接运行 Agent 镜像:
|
||||
|
||||
```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/tcp -p 443:443/udp \
|
||||
-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
|
||||
```
|
||||
|
||||
### 方式 B:执行安装脚本(本地部署)
|
||||
|
||||
在代理节点上执行安装脚本。
|
||||
|
||||
使用 `discovery_token`:
|
||||
|
||||
@@ -75,13 +146,56 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/inst
|
||||
--agent-token YOUR_AGENT_TOKEN
|
||||
```
|
||||
|
||||
安装脚本默认把 Agent 放在 `/opt/openflare-agent`,创建 `openflare-agent.service`,并在未显式配置本机 OpenResty 时使用 Docker OpenResty。
|
||||
脚本默认会:
|
||||
|
||||
## 发布第一份配置
|
||||
| 项目 | 默认值 |
|
||||
| --- | --- |
|
||||
| 安装目录 | `/opt/openflare-agent` |
|
||||
| 配置文件 | `/opt/openflare-agent/agent.json` |
|
||||
| systemd 服务 | `openflare-agent.service` |
|
||||
| OpenResty 路径 | 未指定时自动查找 `openresty` |
|
||||
|
||||
1. 在管理端新增网站配置,填写域名与源站地址。
|
||||
2. 发布前查看预览或变更摘要。
|
||||
3. 激活新版本。
|
||||
4. 等待 Agent 通过 heartbeat 发现版本变更并应用。
|
||||
确认 Agent 服务状态:
|
||||
|
||||
```bash
|
||||
systemctl status openflare-agent
|
||||
journalctl -u openflare-agent -f
|
||||
```
|
||||
|
||||
如果没有 systemd,脚本会输出手动启动命令。
|
||||
|
||||
## 4. 后续步骤
|
||||
|
||||
完成控制面板启动和 Agent 节点接入后,你已经成功搭建好了 OpenFlare 网关的基础运行环境。接下来你可以按顺序继续阅读以下两份指南,开始部署你的第一个反代站点:
|
||||
|
||||
1. **发布第一个网站**:
|
||||
* 请参阅 [发布第一份配置](./first-site.md)。它将引导你以最简单的方式(使用纯 HTTP)发布你的第一条代理规则,并验证节点落地状态。
|
||||
2. **完整配置反向代理(HTTPS 与源站管理)**:
|
||||
* 请参阅 [新建反代配置](./proxy-config.md)。它将指导你从证书导入与申请开始,配置域名 HTTPS 证书绑定、源站管理并预览发布。
|
||||
|
||||
|
||||
## 常见失败原因
|
||||
|
||||
| 现象 | 排查方向 |
|
||||
| --- | --- |
|
||||
| 浏览器打不开管理端 | 确认 `docker compose ps` 中 Server 正在运行,宿主机 `3000` 端口没有被占用 |
|
||||
| 登录后数据无法保存 | 检查 PostgreSQL 容器健康状态,以及 `DSN` 中的用户名、密码、库名是否一致 |
|
||||
| Agent 无法注册 | 确认 Agent 节点能访问 `--server-url`,并检查 Token 是否填错或已失效 |
|
||||
| Agent 在线但没有应用配置 | 确认网站配置已启用,并且已经发布并激活版本 |
|
||||
| OpenResty 应用失败 | 查看节点应用记录和 `journalctl -u openflare-agent`,重点检查域名、证书、上游地址和端口占用 |
|
||||
|
||||
更多排查路径见 [故障排查](./troubleshooting.md)。
|
||||
|
||||
---
|
||||
|
||||
## 进阶部署指引
|
||||
|
||||
当您完成快速开始并熟悉了 OpenFlare 的基本操作后,可以阅读以下进阶部署文档,将各组件投入到正式生产环境中:
|
||||
|
||||
* **Server 生产部署**:阅读 [启动 Server](../deployment/server.md) 了解如何从源码构建前端、配置系统环境变量及使用 Docker Compose 运行。
|
||||
* **Agent 生产接入**:阅读 [部署 Agent](../deployment/agent.md) 了解基于 systemd 的服务管理、详细本地配置文件字段及故障排查。
|
||||
* **内网穿透中继端部署**:阅读 [部署 Relay](../deployment/relay.md) 了解如何为穿透隧道配置公网中继节点(frps)。
|
||||
* **内网穿透客户端部署**:阅读 [部署 OpenFlared](../deployment/openflared.md) 了解如何在内网服务器侧运行穿透守护客户端(frpc)。
|
||||
* **生产部署拓扑参考**:阅读 [部署说明](../deployment/deployment.md) 了解生产高可用拓扑和整体网络规划。
|
||||
* **系统升级与日常维护**:阅读 [升级与维护](../deployment/upgrade.md) 了解如何平滑升级 Server 和各代理节点 Agent。
|
||||
|
||||
版本号格式为 `YYYYMMDD-NNN`。历史版本不可变,回滚通过重新激活旧版本完成。
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
# 启动 Server
|
||||
|
||||
OpenFlare Server 是 Gin + GORM 单体控制面,负责管理端 UI、管理 API、Agent API、配置渲染、版本发布与状态存储。
|
||||
|
||||
## 前置条件
|
||||
|
||||
| 项目 | 要求 |
|
||||
| --- |-----------------------------------|
|
||||
| Go | `1.25+` |
|
||||
| Node.js | `18+` |
|
||||
| 数据库 | SQLite 文件目录可写,或可访问的 PostgreSQL 实例 |
|
||||
|
||||
生产环境建议显式配置 `SESSION_SECRET`,并优先使用 PostgreSQL。
|
||||
|
||||
## 构建管理端前端
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
`pnpm build` 会生成供 Go Server 托管的静态产物。
|
||||
|
||||
## 源码启动
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
export SESSION_SECRET='replace-with-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
# 可选:设置后优先使用 PostgreSQL。
|
||||
# export DSN='postgres://openflare:secret@127.0.0.1:5432/openflare?sslmode=disable'
|
||||
go run .
|
||||
```
|
||||
|
||||
默认监听 `3000` 端口。也可以通过命令行指定:
|
||||
|
||||
```bash
|
||||
go run . --port 3000 --log-dir ./logs
|
||||
```
|
||||
|
||||
## 首次登录
|
||||
|
||||
访问 `http://localhost:3000`。
|
||||
|
||||
| 用户名 | 密码 |
|
||||
| --- | --- |
|
||||
| `root` | `123456` |
|
||||
|
||||
## Swagger
|
||||
|
||||
登录管理端后访问:
|
||||
|
||||
```text
|
||||
http://localhost:3000/swagger/index.html
|
||||
```
|
||||
|
||||
如需在本地重新生成 Swagger 文档:
|
||||
|
||||
```bash
|
||||
go install github.com/swaggo/swag/cmd/swag@v1.16.4
|
||||
cd openflare_server
|
||||
swag init -g main.go -o docs
|
||||
```
|
||||
@@ -1,5 +1,7 @@
|
||||
# SSO 登录配置
|
||||
|
||||
你会学到:如何为 OpenFlare 配置 GitHub OAuth 或标准 OIDC 登录入口,如何填写回调地址,以及第三方账号如何绑定本地用户。
|
||||
|
||||
OpenFlare 支持通过认证源配置第三方登录入口。当前支持 GitHub OAuth 与标准 OIDC Provider,例如 Logto、authentik、Keycloak、Casdoor 等。
|
||||
|
||||
认证源配置完成并启用后,会显示在登录页的第三方账号登录区域。用户可以通过第三方账号登录,也可以在已登录状态下把第三方账号绑定到当前本地账号。
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
# 故障排查
|
||||
|
||||
你会学到:如何按症状排查 OpenFlare Server、数据库、登录、Agent、OpenResty、配置发布和前端构建问题。
|
||||
|
||||
排查时先确认问题发生在哪一层:浏览器、Server、数据库、Agent、OpenResty、源站或 DNS。OpenFlare 的配置不会直接在线写入所有节点,只有激活版本变化后,Agent 才会在 heartbeat 中发现并应用。
|
||||
|
||||
## 快速定位
|
||||
|
||||
| 现象 | 先看哪里 |
|
||||
| --- | --- |
|
||||
| 管理端打不开 | Server 容器或进程日志、端口监听 |
|
||||
| 登录异常 | 默认账号、OPENFLARE_TOKEN、浏览器请求、Server 日志 |
|
||||
| 数据无法保存 | 数据库连接、SQLite 文件权限、PostgreSQL 健康状态 |
|
||||
| Agent 离线 | Agent 日志、Token、Server 地址、网络连通性 |
|
||||
| 发布后节点未更新 | 激活版本、节点 heartbeat、应用记录 |
|
||||
| OpenResty 应用失败 | 应用记录、Agent 日志、证书、上游地址、端口占用 |
|
||||
| 访问分析无数据 | OpenResty 容器状态、观测端口、Agent 补报日志 |
|
||||
|
||||
## Server 无法启动
|
||||
|
||||
1. 查看日志:
|
||||
|
||||
```bash
|
||||
docker compose logs -n 200 openflare
|
||||
```
|
||||
|
||||
源码运行时查看终端输出。
|
||||
|
||||
2. 检查端口占用:
|
||||
|
||||
```bash
|
||||
lsof -i :3000
|
||||
```
|
||||
|
||||
3. 如果使用 PostgreSQL,确认数据库健康:
|
||||
|
||||
```bash
|
||||
docker compose ps postgres
|
||||
docker compose logs -n 100 postgres
|
||||
```
|
||||
|
||||
4. 如果使用 SQLite,确认数据库文件目录可写:
|
||||
|
||||
```bash
|
||||
ls -ld "$(dirname /path/to/openflare.db)"
|
||||
```
|
||||
|
||||
常见原因:
|
||||
|
||||
| 日志或现象 | 处理 |
|
||||
| --- | --- |
|
||||
| 数据库连接失败 | 检查 `DSN` 中用户名、密码、主机、端口、库名和 `sslmode` |
|
||||
| SQLite 无法创建文件 | 检查 `SQLITE_PATH` 所在目录是否存在且可写 |
|
||||
| 端口被占用 | 修改 `PORT` 或 `--port`,或停止占用端口的进程 |
|
||||
|
||||
## 管理端打不开或空白
|
||||
|
||||
1. 确认 Server 正在监听:
|
||||
|
||||
```bash
|
||||
curl -I http://127.0.0.1:3000
|
||||
```
|
||||
|
||||
2. 如果是源码运行,确认已经构建前端静态产物:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
pnpm build
|
||||
```
|
||||
|
||||
3. 检查浏览器访问地址是否与反向代理配置一致。
|
||||
|
||||
4. 如果通过前端开发服务器访问,确认后端代理地址:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
NEXT_DEV_BACKEND_URL=http://127.0.0.1:3000 pnpm dev
|
||||
```
|
||||
|
||||
## 默认账号无法登录
|
||||
|
||||
默认账号是 `root` / `123456`。首次登录后如果已经修改密码,应使用修改后的密码。
|
||||
|
||||
排查步骤:
|
||||
|
||||
1. 确认连接的是预期数据库,避免 `SQLITE_PATH` 或 `DSN` 指向了另一个环境。
|
||||
2. 查看 Server 日志中使用的是 `sqlite` 还是 `postgres`。
|
||||
3. 在浏览器开发者工具中确认管理端 API 请求携带 `OPENFLARE_TOKEN` 请求头。
|
||||
4. 清理浏览器本地存储中的旧 `openflare_token` 后重新登录。
|
||||
|
||||
### 应急重置管理员密码
|
||||
|
||||
如果忘记了 `root` 账户的密码,可以通过直接更新数据库中的密码哈希值将其重置为 `123456`(登录后请务必立即修改):
|
||||
|
||||
#### 1. 若使用 SQLite 数据库
|
||||
停止 Server 运行,使用 sqlite3 客户端打开数据库文件:
|
||||
```bash
|
||||
sqlite3 /path/to/openflare.db
|
||||
```
|
||||
执行以下 SQL 语句:
|
||||
```sql
|
||||
UPDATE users SET password_hash = '$2a$10$wN9aE3zTz83rO7R1uKlhuehJtA3c604pX4Z12B/9.5c0X337t1L4m' WHERE username = 'root';
|
||||
```
|
||||
输入 `.exit` 退出并重新启动 Server。
|
||||
|
||||
#### 2. 若使用 PostgreSQL 数据库
|
||||
通过您的数据库连接工具(如 psql、pgAdmin 或 DBeaver)连接到 PostgreSQL 实例,选择对应的 `openflare` 数据库,执行以下 SQL 语句:
|
||||
```sql
|
||||
UPDATE users SET password_hash = '$2a$10$wN9aE3zTz83rO7R1uKlhuehJtA3c604pX4Z12B/9.5c0X337t1L4m' WHERE username = 'root';
|
||||
```
|
||||
执行成功后即可使用默认密码 `123456` 重新登录管理后台。
|
||||
|
||||
## Agent 无法注册或一直离线
|
||||
|
||||
在 Agent 节点执行:
|
||||
|
||||
```bash
|
||||
curl -I http://your-server:3000
|
||||
```
|
||||
|
||||
查看 Agent 日志:
|
||||
|
||||
```bash
|
||||
journalctl -u openflare-agent -n 200 --no-pager
|
||||
```
|
||||
|
||||
检查配置文件:
|
||||
|
||||
```bash
|
||||
sed -n '1,160p' /opt/openflare-agent/agent.json
|
||||
```
|
||||
|
||||
重点确认:
|
||||
|
||||
| 配置 | 说明 |
|
||||
| --- | --- |
|
||||
| `server_url` | 必须是 Agent 节点能访问的 Server 地址 |
|
||||
| `agent_token` / `discovery_token` | 至少填写一个 |
|
||||
| `heartbeat_interval` | 支持毫秒整数或 Go duration 字符串 |
|
||||
| `request_timeout` | 网络较慢时可适当增大 |
|
||||
|
||||
如果日志提示 Token 无效,重新在管理端准备 Token 并更新 `agent.json`,然后重启:
|
||||
|
||||
```bash
|
||||
systemctl restart openflare-agent
|
||||
```
|
||||
|
||||
## 发布后节点没有应用新版本
|
||||
|
||||
按顺序检查:
|
||||
|
||||
1. 版本页面中是否已经激活目标版本。
|
||||
2. 节点是否在线,最近心跳时间是否更新。
|
||||
3. 应用记录中是否有目标版本的成功、警告或失败记录。
|
||||
4. 网站配置是否启用;未启用的网站不会参与发布渲染。
|
||||
5. Agent 日志是否出现拉取、校验、reload 或回滚信息。
|
||||
|
||||
查看 Agent 日志:
|
||||
|
||||
```bash
|
||||
journalctl -u openflare-agent -f
|
||||
```
|
||||
|
||||
注意:某个目标 `version + checksum` 一旦应用失败并回退,Agent 会在本地状态中阻断该目标重复应用。修正配置后需要重新发布生成新的 checksum,或激活旧版本回滚。
|
||||
|
||||
如果这是 Agent 首次应用配置,且本地没有历史 `nginx.conf` 可回滚,失败目标仍会被阻断,但 Agent 会尝试进入安全兜底运行态。此时应用记录和 Agent 日志会包含 `fallback runtime started`,OpenResty 对外只监听 `80` 端口并统一返回 `503` 与 `OpenFlare: No Valid Configuration`,同时保留本地 `stub_status` 健康检查入口。修正配置并重新发布新版本后,Agent 会覆盖兜底配置并恢复正常代理。
|
||||
|
||||
## OpenResty 应用失败
|
||||
|
||||
常见原因:
|
||||
|
||||
| 原因 | 排查 |
|
||||
| --- | --- |
|
||||
| 域名或 server 块冲突 | 检查同一域名是否被多个网站配置使用 |
|
||||
| 上游地址不合法 | 确认所有上游都是 `http://` 或 `https://` |
|
||||
| 多上游格式不符合约束 | 多上游必须是纯 `scheme://host[:port]` |
|
||||
| 证书缺失或路径错误 | 检查域名是否绑定证书,以及 Agent 证书目录是否可写 |
|
||||
| 端口被占用 | 检查本机 `80`、`443` 端口 |
|
||||
|
||||
OpenResty 配置校验:
|
||||
|
||||
```bash
|
||||
openresty -t -c /path/to/openflare/data/etc/nginx/nginx.conf
|
||||
```
|
||||
|
||||
OpenResty 运行状态:
|
||||
|
||||
```bash
|
||||
ps aux | grep openresty
|
||||
```
|
||||
|
||||
Agent 周期性健康检查通过本地 `http://127.0.0.1:<openresty_observability_port>/openflare/stub_status` 判断 OpenResty 是否存活,不会反复执行 `openresty -t`。如果节点被标记为 unhealthy,优先确认该本地观测端口是否正在监听;如果只在应用配置时出现 `host not found in upstream`,说明失败来自配置校验或 reload,而不是周期性健康探针。
|
||||
|
||||
实际二进制路径和主配置路径以 `agent.json` 中的 `openresty_path` 与 `main_config_path` 为准。
|
||||
|
||||
## HTTPS 不生效
|
||||
|
||||
1. 确认证书已经上传或托管。
|
||||
2. 确认网站配置中对应域名已经绑定证书。
|
||||
3. 确认发布并激活了新版本。
|
||||
4. 查看应用记录是否成功。
|
||||
5. 用 `curl` 查看证书和状态码:
|
||||
|
||||
```bash
|
||||
curl -Iv https://your-domain
|
||||
```
|
||||
|
||||
没有绑定证书的域名不会被自动加入 HTTPS 配置,这是预期行为。
|
||||
|
||||
## 访问分析没有数据
|
||||
|
||||
1. 确认节点已经成功应用包含观测 Lua 资源的配置。
|
||||
2. 确认 OpenResty 正在运行。
|
||||
3. 查看 Agent 日志是否有观测采集或补报失败信息。
|
||||
4. 检查 `openresty_observability_port` 是否被占用,默认是 `18081`。
|
||||
5. 确认 Server 侧没有因数据库清理策略删除对应时间窗口数据。
|
||||
|
||||
## 前端构建失败
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
corepack enable
|
||||
pnpm install
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
pnpm build
|
||||
```
|
||||
|
||||
常见原因:
|
||||
|
||||
| 现象 | 处理 |
|
||||
| --- | --- |
|
||||
| pnpm 版本不一致 | 使用 `corepack enable` 后重新安装 |
|
||||
| 类型错误 | 先运行 `pnpm typecheck` 定位具体文件 |
|
||||
| API 类型不一致 | 检查 `lib/api/` 和 `types/` 中的响应结构 |
|
||||
| E2E 失败 | 确认 Server 和前端开发服务器都已启动 |
|
||||
|
||||
## 文档站构建失败
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
如果是链接错误,检查新增页面是否已经加入 `docs/config.ts` 侧边栏,或者相对链接是否指向存在的 Markdown 文件。
|
||||
@@ -0,0 +1,174 @@
|
||||
# 内网穿透与隧道使用
|
||||
|
||||
你会学到:OpenFlare 内网穿透隧道的设计原理、核心概念(中继节点与隧道客户端),以及如何从零开始将内网开发环境或私有云服务一步步安全、稳定地发布到公网域名上。
|
||||
|
||||
在许多实际开发和运维场景中,我们的源站服务部署在局域网、本地开发机或防范严密的私有 VPC 内部,没有公网 IP,亦无法在边界防火墙或路由器上配置端口映射。
|
||||
|
||||
OpenFlare 提供了**基于反向中继穿透隧道**的整体解决方案。你只需在内网环境发起向公网中继节点的出向安全连接,无需配置任何入方向端口,即可将公网的 Web 访问流量平滑引入内网源站,同时享有网关提供的 TLS 证书自动托管与 WAF 安全防护。
|
||||
|
||||
---
|
||||
|
||||
## 核心概念
|
||||
|
||||
在使用内网穿透功能前,你需要熟悉以下组件与核心概念:
|
||||
|
||||
| 概念 | 说明 | 对应组件/操作 |
|
||||
| --- | --- | --- |
|
||||
| **中继节点 (Relay)** | 部署在公网边缘的流量中继服务,负责监听内网客户端的长连接,并作为网关 Agent (OpenResty) 与内网流量的中转桥梁。 | 运行 `openflare-relay` 守护的 `tunnel_relay` 节点 |
|
||||
| **穿透隧道 (Tunnel)** | 逻辑上的穿透客户端实例,拥有全局唯一 ID 与安全认证令牌,用以标识一个具体的内网环境。 | 由 Server 随机生成 `tunnel_id` (tun-<32hex>) |
|
||||
| **隧道客户端 (Client)** | 运行在内网环境下的轻量控制器,根据 Server 下发的配置自动管理底层的 frpc 隧道子进程。 | 内网部署的 `openflared` 容器或独立二进制进程 |
|
||||
| **隧道上游 (Tunnel Upstream)** | 网站配置中的特殊上游类型。选择此类型后,网关会将公网流量转发至本地中继端的 Vhost 端口,最终送达内网源站。 | 网站详情中配置的 `tunnel` 类型上游 |
|
||||
|
||||
---
|
||||
|
||||
## 推荐操作顺序
|
||||
|
||||
将一个内网服务发布到公网,推荐按这个顺序进行:
|
||||
|
||||
1. 注册并部署至少一个公网 **中继节点 (Relay)** 并保持在线。
|
||||
2. 在管理端创建 **穿透隧道 (Tunnel)** 并复制对应的专属 Token。
|
||||
3. 在内网服务器中部署并启动 **隧道客户端 (OpenFlared)**。
|
||||
4. 确认管理端中该隧道的在线状态显示为「在线」。
|
||||
5. 新增网站配置,上游类型选择 **内网穿透**,绑定对应隧道并填写内网端口(如 `127.0.0.1:8080`)。
|
||||
6. 发布并激活新版本。
|
||||
7. 通过公网域名访问,验证内网穿透链路是否打通。
|
||||
|
||||
---
|
||||
|
||||
## 详细配置步骤
|
||||
|
||||
### 第一步:准备中继节点 (Relay)
|
||||
|
||||
内网流量需要通过公网的中继节点进行中转。在开始前,你需要确保公网有一台可用的中继服务器。
|
||||
|
||||
1. 登录管理端,进入 **「节点管理」**。
|
||||
2. 添加一个新节点,并将 **节点类型** 选择为 **中继节点 (tunnel_relay)**。
|
||||
3. 保存后,复制该节点专属的 `agent_token`。
|
||||
4. 在你的公网服务器上启动 `openflare-relay`。你可以直接使用 Docker 快速运行:
|
||||
|
||||
```bash
|
||||
docker run -d --name openflare-relay --restart unless-stopped \
|
||||
-p 7000:7000 \
|
||||
-e OPENFLARE_SERVER_URL=http://<你的Server公网IP>:3000 \
|
||||
-e OPENFLARE_AGENT_TOKEN=<刚才复制的AgentToken> \
|
||||
-v openflare-relay-data:/var/lib/openflare-relay \
|
||||
ghcr.io/rain-kl/openflare-relay:latest
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 请务必在云服务器安全组中放行 `7000` 端口(frpc 客户端连接控制端口)。如果你的 Server 与中继节点部署在同一台机器,这里的 `OPENFLARE_SERVER_URL` 应指向 Server 的公网或内网通信 IP。
|
||||
|
||||
### 第二步:在管理端创建穿透隧道
|
||||
|
||||
1. 导航至管理侧边栏的 **「内网穿透」** 页面。
|
||||
2. 点击 **「创建隧道」** 按钮,在弹窗中填写:
|
||||
* **隧道名称**:描述此内网环境,例如 `home-lab` 或 `office-dev`。
|
||||
* **描述**:可选填,描述此隧道的具体用途。
|
||||
3. 点击保存后,系统将自动生成该隧道的全局唯一 ID 与一串专属的 `tunnel_token`(形如 `tun-xxxx...`)。
|
||||
4. 复制弹窗中为你生成的 **客户端部署命令**,用于下一步内网环境的部署。
|
||||
|
||||
### 第三步:部署内网客户端 (OpenFlared)
|
||||
|
||||
回到你的内网服务器中,根据刚才复制的部署命令运行客户端。
|
||||
|
||||
#### 方案 A:使用 Docker 部署(强烈推荐)
|
||||
|
||||
官方提供的 `openflared` 镜像已经内置了主控守护进程与 `frpc` 运行时,开箱即用,无需配置额外依赖:
|
||||
|
||||
```bash
|
||||
docker run -d --name openflared --restart unless-stopped \
|
||||
-e OPENFLARE_SERVER_URL=http://<你的Server公网IP>:3000 \
|
||||
-e OPENFLARE_TUNNEL_TOKEN=<刚才复制的TunnelToken> \
|
||||
-v openflared-data:/app/data \
|
||||
ghcr.io/rain-kl/openflared:latest
|
||||
```
|
||||
|
||||
#### 方案 B:宿主机二进制手动运行
|
||||
|
||||
如果你不便使用 Docker,也可以下载或自行编译 `flared` 二进制程序:
|
||||
|
||||
1. 在内网机器的程序同级目录下创建 `flared.json` 配置文件:
|
||||
```json
|
||||
{
|
||||
"server_url": "http://<你的Server公网IP>:3000",
|
||||
"tunnel_token": "<刚才复制的TunnelToken>",
|
||||
"frpc_path": "/usr/local/bin/frpc",
|
||||
"data_dir": "./data"
|
||||
}
|
||||
```
|
||||
2. 执行启动命令:
|
||||
```bash
|
||||
./flared -config ./flared.json
|
||||
```
|
||||
|
||||
#### 状态确认
|
||||
|
||||
启动成功后,内网客户端会通过出向网络向控制面发送心跳同步配置。此时:
|
||||
1. 刷新管理端的 **「内网穿透」** 列表,刚才创建的隧道状态指示灯应当变为绿色的 **「在线」**。
|
||||
2. 点击隧道详情,你可以直观地查看到当前内网客户端连接了公网的哪些中继 Relay 节点。
|
||||
|
||||
### 第四步:创建网站并绑定隧道上游
|
||||
|
||||
现在你可以为你的内网服务配置公网反向代理和域名访问了。
|
||||
|
||||
1. 进入 **「网站配置」** 页面,点击 **「新建网站」**。
|
||||
2. 填写公网访问该网站所需的 **域名**,例如 `nas.example.com`。
|
||||
3. 关键配置:在 **「上游配置」** 区域,将 **上游类型** 从默认的「直连」切换为 **「内网穿透」**。
|
||||
4. 在下拉列表中选择你刚刚部署上线的 **内网隧道**(如 `home-lab`)。
|
||||
5. 填写 **内网目标地址**(对于内网客户端来说可访问的本地地址与端口,例如 `127.0.0.1:8080`)与 **内网协议**(通常为 `http`)。
|
||||
6. 配置其他站点常规项(如 TLS 证书等),并点击保存。
|
||||
|
||||
### 第五步:发布与生效
|
||||
|
||||
为了让网关的 OpenResty 能够正确匹配并路由域名流量,我们需要发布新的配置版本。
|
||||
|
||||
1. 点击导航栏右上角的 **「配置预览」**,确认生成的站点配置无误。
|
||||
2. 在弹出窗口中,点击 **「发布并激活」**。
|
||||
3. 此时,公网边缘的 Agent 会拉取到最新路由:它会将 `nas.example.com` 的请求转发至同机部署的 `openflare-relay (frps)` 的虚拟主机端口下。
|
||||
4. 内网客户端 `openflared (frpc)` 会接收到被中继的封包,并安全地透传给内网的 `127.0.0.1:8080` 服务,最后原路返回响应。
|
||||
5. 在你的公网浏览器中访问 `nas.example.com`,确认内网服务成功展示!
|
||||
|
||||
---
|
||||
|
||||
## 高级应用场景
|
||||
|
||||
### 1. 单隧道多服务复用 (多端口映射)
|
||||
|
||||
你并不需要为内网的每一个服务都部署一个 `openflared` 容器。
|
||||
|
||||
如果你想在一个内网环境映射多个不同的服务(例如:`127.0.0.1:80` 是博客,`127.0.0.1:8080` 是 API,`192.168.1.120:9000` 是内网网盘):
|
||||
1. 保持这一个 `openflared` 客户端在线。
|
||||
2. 在管理端创建三个独立的网站配置(绑定各自对应的公网域名)。
|
||||
3. 这三个网站配置都将 **上游类型** 选为 **同一个穿透隧道**。
|
||||
4. 分别在各自的“内网目标地址”中填入对应不同的端口或局域网 IP(例如 `127.0.0.1:80`、`127.0.0.1:8080`、`192.168.1.120:9000`)。
|
||||
5. 发布并激活新版本,即可实现一隧多用。
|
||||
|
||||
### 2. 网关安全功能无缝叠加
|
||||
|
||||
因为所有公网流量均首先进入公网的 Agent 节点,在此处完成了 HTTPS/TLS 握手与 WAF 引擎拦截,然后再通过安全隧道送达内网。
|
||||
|
||||
因此,你的内网服务**天然且无需做任何改造**即可享受以下高级特性:
|
||||
* **一键启用 HTTPS**:直接在管理端为域名选择或申请 SSL 证书,数据传输全程加密。
|
||||
* **全局/自定义 WAF 防护**:开启 SQL 注入拦截、XSS 注入防御与恶意地域 IP 屏蔽。
|
||||
* **人机挑战 (CC PoW)**:一键抵御针对内网服务的恶意 CC 刷接口攻击。
|
||||
|
||||
---
|
||||
|
||||
## 常见故障排查
|
||||
|
||||
### 1. 隧道在管理端显示为「离线」
|
||||
|
||||
* **检查 Token 是否正确**:查看 `flared` 日志或环境变量中配置的 `tunnel_token` 是否与管理端生成的一致。
|
||||
* **检查网络连通性**:内网服务器需能通过出向网络正常请求 Server 地址。确保控制面没有启用防火墙限制客户端的 HTTP 请求。
|
||||
* **中继节点防火墙未开**:检查对应中继节点的公网 `7000` 端口(或你自定义的 bindPort)是否已经在安全组中对公网放行。
|
||||
|
||||
### 2. 访问公网域名返回 502 Bad Gateway / 504 Gateway Timeout
|
||||
|
||||
* **内网服务未运行**:确认内网目标地址对应的服务已在内网服务器上成功启动并处于监听状态。
|
||||
* **目标地址不可达**:如果内网地址填的是 `127.0.0.1:8080`,确保服务确实在运行着 `openflared` 的同一台主机上;如果填的是局域网 IP `192.168.x.x`,请在 `openflared` 容器内测试该局域网 IP 的连通性。
|
||||
* **检查客户端应用日志**:在管理端查看「应用记录」或在内网查看 `flared` 运行日志,排查是否有 `LastError` 产生。frpc 在连不上内网端口时,会将连接失败报错原样上报至 Server 方便管理员定位。
|
||||
|
||||
### 3. 多中继网络动荡或重试失败
|
||||
|
||||
* 当控制面关联了多个 Relay 中继节点时,`openflared` 会为每个 Relay 独立派生 frpc 守护进程,并在 `flared.json` 中配置的 `sync_interval`(默认 30s)内定时向控制面拉取拓扑状态。
|
||||
* 若发现某一中继节点频繁由于网络抖动离线,系统会自动触发退避重试机制。你可以在宿主机日志中看到 `frpc process missing, starting` 的日志,这属于正常的进程自愈逻辑,通常在网络恢复后 5~10 秒内即可自动恢复建连。
|
||||
@@ -0,0 +1,49 @@
|
||||
# Uptime Kuma 监控同步
|
||||
|
||||
你会学到:如何启用并配置 Uptime Kuma 自动同步集成,控制监测站点的同步范围与心跳探测参数,以及 OpenFlare 与 Uptime Kuma 差分同步的底层原理。
|
||||
|
||||
---
|
||||
|
||||
## 功能概述
|
||||
|
||||
在边缘多节点运维中,及时了解各个代理站点的可用性至关重要。为了避免手动在监控系统中重复录入站点信息,OpenFlare 提供了与开源监控服务 **Uptime Kuma** 的深度集成。
|
||||
|
||||
启用集成后,OpenFlare 会启动一个后台同步调度器,自动将管理端配置的代理站点同步为 Uptime Kuma 中的 HTTP 监控任务。支持检测范围过滤、差分属性更新以及对下线站点的自动清理。
|
||||
|
||||
---
|
||||
|
||||
## 第一步:在系统设置中配置集成
|
||||
|
||||
1. 登录管理端控制面板,进入左侧导航 **「系统设置」** -> **「Uptime Kuma 集成」**(或通过控制台中的集成配置入口)。
|
||||
2. 配置以下核心连接参数:
|
||||
* **启用状态 (Enabled)**:开启集成开关。
|
||||
* **实例地址 (Instance URL)**:你的 Uptime Kuma 服务地址。例如 `http://192.168.1.100:3001` 或 `https://kuma.example.com`(必须包含协议前缀 `http://` 或 `https://`)。
|
||||
* **用户名 (Username)** 与 **密码 (Password)**:具有管理权限的 Uptime Kuma 账户凭证,用于 API 鉴权。
|
||||
|
||||
---
|
||||
|
||||
## 第二步:控制监控范围与心跳参数
|
||||
|
||||
在集成面板中,你可以对监控范围和具体探测行为进行细粒度控制:
|
||||
|
||||
### 1. 监控范围 (Monitor Scope)
|
||||
* **全部站点 (All)**:默认选项。OpenFlare 将自动同步所有**已启用**的代理路由站点。当新站点被创建且启用,或者旧站点被停用时,监控列表将自动增删。
|
||||
* **选择站点 (Selected)**:仅监控指定站点。选择此模式后,可以点击 **「选择监控站点」** 弹出框。在弹出框内可以通过搜索过滤站点并进行勾选。被取消勾选或未勾选的站点将不会被同步(若已存在则会被自动清理)。
|
||||
|
||||
### 2. 监测频率与心跳设置
|
||||
你可以为自动生成的监控项指定统一的探测参数:
|
||||
* **同步间隔 (Sync Interval)**:自动差分同步的频率(分钟),默认为 `5` 分钟。即控制面每 5 分钟与 Uptime Kuma 进行一次状态比对。
|
||||
* **心跳检测频率 (Interval)**:Uptime Kuma 探测站点的频率(秒),默认为 `60` 秒。
|
||||
* **最大重试次数 (Retry)**:探测失败后,判定为 Down 之前的最大重试次数,默认为 `0`。
|
||||
* **重试间隔时间 (Retry Interval)**:重试之间的等待秒数,默认为 `60` 秒。
|
||||
* **请求超时时间 (Timeout)**:探测请求判定为超时的秒数,默认为 `48` 秒。
|
||||
|
||||
---
|
||||
|
||||
## 同步与清理机制
|
||||
|
||||
* **专属标签隔离**:所有自动创建的监控项均会绑定 `OpenFlare` 专属标签(紫蓝色)。同步和清理程序仅操作带有该标签的监控任务,**绝不干扰或破坏你在 Uptime Kuma 中手动创建的其他监控项**。
|
||||
* **差分增量同步**:同步程序会周期性对比监控元数据。当检测到域名或心跳配置变更时,仅执行差分更新,避免中断历史统计数据;当站点停用或移出范围时,会自动执行监控下线清理。
|
||||
|
||||
> [!TIP]
|
||||
> 关于 Uptime Kuma 监控同步的 Socket.IO 控制流、防污染标签模型及差分比对算法细节,请参阅 [Uptime Kuma 监控同步设计](../design/kuma-design.md)。
|
||||
@@ -0,0 +1,168 @@
|
||||
# WAF 自动 IP 组规则语法
|
||||
|
||||
自动 IP 组用于从请求日志中按单个客户端 IP 聚合指标,再用 Expr 表达式判断是否把该 IP 加入组内名单。自动 IP 组可以被 WAF 规则组的 IP 黑名单或白名单引用;发布配置时,Server 只把 IP 组引用 ID 写入 `waf_config.json`,IP 组成员由 Agent 独立同步到本地运行时文件。
|
||||
|
||||
## 配置结构
|
||||
|
||||
自动 IP 组的配置是一个 JSON 对象:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 60,
|
||||
"rules": [
|
||||
{
|
||||
"name": "单 IP 404 高频扫描",
|
||||
"expr": "request_count > 100 && StatusRatio(404) >= 0.8"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
| 字段 | 类型 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| `lookback_minutes` | number | 每次执行时回看多少分钟内的请求日志。未填写时默认 60 分钟,最小 5 分钟,最大 43200 分钟。 |
|
||||
| `rules` | array | 自动规则列表。任意一条规则命中时,该 IP 会进入自动 IP 组名单。 |
|
||||
| `rules[].name` | string | 规则名称,只用于界面展示和错误提示。 |
|
||||
| `rules[].expr` | string | Expr 表达式,必须返回布尔值。 |
|
||||
|
||||
## 执行口径
|
||||
|
||||
自动规则不是逐条请求判断,而是先按单个客户端 IP 聚合:
|
||||
|
||||
1. Server 读取最近 `lookback_minutes` 分钟内的请求日志。
|
||||
2. 按 `remote_addr` 归一化后的 IP 分组。
|
||||
3. 为每个 IP 计算请求数、404 数、直连 IP Host 次数等指标。
|
||||
4. 逐个 IP 执行 `rules[].expr`。
|
||||
5. 只要某个 IP 命中任意规则,就写入该自动 IP 组的 `IP / IP 段` 列表。
|
||||
|
||||
Host 是否为“通过 IP 访问”按请求日志中的 `Host` 字段判断:如果 Host 是 IPv4 或 IPv6 字面量,例如 `203.0.113.10`、`[2001:db8::10]`、`203.0.113.10:443`,就计入 `ip_host_count`。
|
||||
|
||||
## 可用关键字
|
||||
|
||||
表达式中可以直接使用以下字段:
|
||||
|
||||
| 关键字 | 类型 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| `ip` | string | 当前正在判断的客户端 IP。 |
|
||||
| `request_count` | number | 当前 IP 在回看窗口内的总请求数。 |
|
||||
| `status_404_count` | number | 当前 IP 在回看窗口内返回 404 的请求数。 |
|
||||
| `status_404_ratio` | number | 404 请求占比,计算方式为 `status_404_count / request_count`。 |
|
||||
| `ip_host_count` | number | 当前 IP 通过 IP 地址作为 Host 访问的请求数。 |
|
||||
| `ip_host_ratio` | number | 通过 IP 地址访问的占比,计算方式为 `ip_host_count / request_count`。 |
|
||||
| `client_error_count` | number | 当前 IP 返回 4xx 状态码的请求数。 |
|
||||
| `server_error_count` | number | 当前 IP 返回 5xx 状态码的请求数。 |
|
||||
| `last_seen_unix` | number | 当前 IP 在回看窗口内最后一次请求的 Unix 秒级时间戳。 |
|
||||
|
||||
比例字段都是 `0` 到 `1` 之间的小数。80% 应写成 `0.8`,50% 应写成 `0.5`。
|
||||
|
||||
### 自定义状态码匹配方法
|
||||
|
||||
如果内置的 `status_404_count` 和 `status_404_ratio` 不能满足您的需求,您可以使用以下内置方法来匹配任意状态码的请求数与占比:
|
||||
|
||||
* **`StatusCount(code)`**: 获取当前 IP 在回看窗口内返回指定状态码的请求数(如 `StatusCount(403) > 10`)
|
||||
* **`StatusRatio(code)`**: 获取当前 IP 在回看窗口内返回指定状态码的请求数占该 IP 总请求数的比例(如 `StatusRatio(502) >= 0.5`)
|
||||
|
||||
## Expr 常用写法
|
||||
|
||||
自动 IP 组使用 Expr 语法,当前表达式必须返回布尔值。
|
||||
|
||||
常用运算符:
|
||||
|
||||
| 写法 | 作用 | 示例 |
|
||||
| --- | --- | --- |
|
||||
| `>`、`>=`、`<`、`<=` | 数值比较 | `request_count > 100` |
|
||||
| `==`、`!=` | 相等或不相等 | `ip != "127.0.0.1"` |
|
||||
| `&&` | 并且 | `request_count > 100 && StatusRatio(404) >= 0.8` |
|
||||
| `||` | 或者 | `StatusRatio(404) >= 0.8 || server_error_count > 20` |
|
||||
| `!` | 取反 | `!(ip == "127.0.0.1")` |
|
||||
| `in` | 判断值是否在列表中 | `ip in ["203.0.113.10", "198.51.100.20"]` |
|
||||
| `not in` | 判断值是否不在列表中 | `ip not in ["127.0.0.1"]` |
|
||||
| `()` | 分组控制优先级 | `(request_count > 100 && StatusRatio(404) >= 0.8) || server_error_count > 50` |
|
||||
|
||||
## 内置预设
|
||||
|
||||
管理端内置两个预设规则,可以直接添加后再按需调整:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "单 IP 404 高频扫描",
|
||||
"expr": "request_count > 100 && StatusRatio(404) >= 0.8"
|
||||
}
|
||||
```
|
||||
|
||||
含义:单个 IP 在回看窗口内请求数大于 100,并且 404 状态码占比不低于 80%。
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "单 IP 直连访问异常",
|
||||
"expr": "ip_host_count > 50 && ip_host_ratio > 0.5"
|
||||
}
|
||||
```
|
||||
|
||||
含义:单个 IP 通过 IP 地址作为 Host 访问的次数大于 50,并且这种访问占比大于 50%。
|
||||
|
||||
## 示例
|
||||
|
||||
高频 404 扫描:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 60,
|
||||
"rules": [
|
||||
{
|
||||
"name": "高频 404 扫描",
|
||||
"expr": "request_count > 100 && StatusRatio(404) >= 0.8"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
IP 直连访问异常:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 30,
|
||||
"rules": [
|
||||
{
|
||||
"name": "IP 直连访问异常",
|
||||
"expr": "ip_host_count > 50 && ip_host_ratio > 0.5"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
同时捕获高 4xx 与高 5xx:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 120,
|
||||
"rules": [
|
||||
{
|
||||
"name": "异常错误率",
|
||||
"expr": "(client_error_count > 80 && request_count > 100) || server_error_count > 30"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
排除可信 IP:
|
||||
|
||||
```json
|
||||
{
|
||||
"lookback_minutes": 60,
|
||||
"rules": [
|
||||
{
|
||||
"name": "排除可信 IP 的 404 扫描",
|
||||
"expr": "ip not in [\"203.0.113.10\", \"198.51.100.20\"] && request_count > 100 && StatusRatio(404) >= 0.8"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 使用建议
|
||||
|
||||
先用较短的回看窗口和较高阈值观察命中结果,再逐步调整阈值。管理端 IP 组页面支持在保存前点击 **测试规则**,直接查看当前回看窗口内命中的 IP;自动 IP 组真正执行后会覆盖该组的 IP 列表。如果要长期保留某些地址,建议放入手动 IP 组,并在 WAF 规则组中同时引用手动组和自动组。
|
||||
|
||||
自动 IP 组更新后不需要重新发布配置版本。在线 Agent 会通过 WebSocket 收到变更 IP 组并更新本地 `waf_ip_groups.json`;WebSocket 不可用时,Agent 会在下一次心跳中上报本地 IP 组 checksum,Server 只返回 checksum 不一致的 IP 组。
|
||||
@@ -0,0 +1,178 @@
|
||||
# WAF 安全防护使用
|
||||
|
||||
你会学到:OpenFlare 边缘 Web 应用防火墙 (WAF) 的工作原理、防护维度,如何管理与引用三类 IP 组(手动、订阅与基于 Expr 的自动 IP 组),配置防 CC 挑战(PoW 人机验证)与地域级拦截,以及如何在不 reload 进程的情况下实现 IP 组成员的秒级热更新。
|
||||
|
||||
---
|
||||
|
||||
## 核心概念
|
||||
|
||||
在配置安全策略前,你需要理解 WAF 的几个核心组成部分:
|
||||
|
||||
| 概念 | 说明 | 作用范围与生效方式 |
|
||||
| --- | --- | --- |
|
||||
| **WAF 规则组 (Rule Group)** | 安全规则的逻辑集合。包括:IP 黑白名单(直接录入或引用 IP 组)、国家/地区地域限制、防 CC 挑战(PoW)以及自定义拦截响应。 | 支持全局生效或绑定到单个/多个网站。**修改规则组定义必须发布并激活配置版本**。 |
|
||||
| **IP 组 (IP Group)** | 存放单个 IP 或 CIDR 网段的列表容器。分为**手动**、**订阅**与**自动**三类。WAF 规则组可通过 ID 引用 IP 组。 | 属于动态资源。**IP 组成员的增减支持 WebSocket 秒级无缝热同步,无需 reload 进程**。 |
|
||||
| **人机挑战 (CC PoW)** | 基于 Proof of Work (工作量证明) 的人机验证挑战。通过让浏览器计算特定难度的哈希碰撞,静默阻断恶意刷接口的自动化脚本与 Bot,保障正常用户体验。 | 位于规则组内的配置 Tab。**修改 PoW 参数必须发布并激活配置版本**。 |
|
||||
|
||||
---
|
||||
|
||||
## 推荐配置顺序
|
||||
|
||||
配置网站的安全防护时,推荐按这个顺序进行:
|
||||
|
||||
1. 进入 IP 组管理,创建所需的 **手动 IP 组** (如开发者白名单) 或 **自动 IP 组** (如根据 404 扫描自动封禁的 IP)。
|
||||
2. 创建或编辑 **WAF 规则组**:
|
||||
* 绑定需要引用或阻断的 IP 组。
|
||||
* 配置国家或省份的地域黑白名单限制。
|
||||
* (可选) 在 `PoW` 标签页配置人机挑战参数。
|
||||
* 在 `拦截返回` 标签页设定自定义状态码(如 403, 418)和 HTML 拦截页。
|
||||
3. 将规则组关联到对应的 **网站配置**。
|
||||
4. 发布并激活配置版本,使边缘节点 (Agent) 开始应用 WAF 规则过滤流量。
|
||||
|
||||
---
|
||||
|
||||
## 详细步骤指南
|
||||
|
||||
### 第一步:管理与配置 IP 组
|
||||
|
||||
IP 组是进行大批量 IP 过滤的基石。OpenFlare 提供了极富弹性的三类 IP 组:
|
||||
|
||||
#### 1. 手动 IP 组 (Manual)
|
||||
* **用途**:静态维护一些确定受信任或确定需长期拦截的 IP/网段。
|
||||
* **配置**:点击「创建 IP 组」-> 类型选择「手动」-> 按行直接填入 IP 或 CIDR 格式(例如 `192.168.1.100` 或 `10.0.0.0/24`)。
|
||||
|
||||
#### 2. 订阅 IP 组 (Subscription)
|
||||
* **用途**:接入第三方开源威胁情报库、云厂商公布的官方网段(如 Cloudflare, GitHub Action IP 列表),或团队内部统一维护的动态 IP 源。
|
||||
* **配置参数**:
|
||||
* **订阅 URL**:必须是合法的 `http` 或 `https` 链接。
|
||||
* **订阅格式**:支持 `Text` 与 `JSON` 两种数据格式:
|
||||
* **Text 格式**:纯文本格式。按行分隔读取 IP/CIDR,会自动过滤掉以 `#` 开头的注释行和空白行。
|
||||
* **JSON 格式**:当订阅源是一个结构化的 JSON 响应时,需要编写 **映射规则 (Mapping Rule)** 从 JSON 数据中提取 IP 列表。
|
||||
* **映射规则**:使用类似 JSONPath 的轻量点语法定位 IP 数组,支持以 `[]` 展开数组。例如:
|
||||
* 若 JSON 结构为 `{"data": {"ips": ["1.1.1.1", "2.2.2.2"]}}`,则映射规则填写 `$.data.ips[]`(或 `data.ips[]`)。
|
||||
* 若 JSON 根节点本身即为字符串数组(如 `["1.1.1.1", "2.2.2.2"]`),映射规则留空或填写 `$` 即可。
|
||||
* **同步间隔 (分钟)**:该订阅组自动同步的周期,默认为 `1440` 分钟(24小时),允许范围为 `5` 至 `43200` 分钟。
|
||||
* **安全限额与同步频率**:
|
||||
* 为防止恶意或超大订阅源造成系统负担,单次抓取上限限制为 **2 MiB**,网络拉取超时为 15 秒。
|
||||
* Server 默认每 5 分钟在后台扫描一次到期的订阅 IP 组并拉取同步。
|
||||
|
||||
> [!TIP]
|
||||
> 关于 WAF 的动态 IP 组异步差分同步模型(WebSocket 实时热同步、不触发 Nginx Reload 机制)以及高性能 Lua 缓存方案等底层设计细节,请参阅 [WAF 设计](../design/waf-design.md)。
|
||||
|
||||
#### 3. 自动 IP 组 (Automatic)
|
||||
* **用途**:**最具杀伤力的防扫描、防爆破自动通道**。
|
||||
* **配置**:类型选择「自动」-> 编写 Expr 日志聚合逻辑。你可以直接引用系统内置的预设:
|
||||
* **单 IP 404 高频扫描**:`request_count > 100 && StatusRatio(404) >= 0.8` (单个 IP 最近一小时请求超 100 次且 404 响应占比超 80%)。
|
||||
* **单 IP 直连访问异常**:`ip_host_count > 50 && ip_host_ratio > 0.5` (绕过域名直接通过 IP 地址进行高频请求)。
|
||||
* **测试与立即执行**:保存前可点击 **「测试规则」** 按钮预览当前日志窗口被命中的 IP。保存后可点击 **「立即执行」** 直接聚合日志并生成封禁名单。
|
||||
|
||||
> [!TIP]
|
||||
> 自动 IP 组的详细语法和可用指标请参阅 [WAF 自动 IP 组规则语法](./waf-ip-group-expr.md)。
|
||||
|
||||
---
|
||||
|
||||
### 第二步:创建与配置 WAF 规则组
|
||||
|
||||
1. 导航至左侧菜单 **「安全防护 (WAF)」**,点击 **「创建规则组」**。
|
||||
2. 填写规则组名称(如 `production-api-shield`),选择是否为「全局规则组」。
|
||||
3. 进入规则组详情,在下方几个配置 Tab 中依次设置:
|
||||
|
||||
#### 1. 黑白名单配置 (Allow / Block Lists)
|
||||
* **直录 IP**:可直接在框内按行填入临时需要白名单放行或黑名单阻断的单个 IP 或网段。
|
||||
* **IP 组引用**:点击「绑定 IP 组」,选择你在第一步中配置好的手动、自动或订阅 IP 组。白名单引用会直接放行,黑名单引用则直接阻断。
|
||||
|
||||
#### 2. 地域限制 (GeoIP)
|
||||
* **说明**:OpenFlare 集成了 GeoIP 地理位置解析。
|
||||
* **配置**:可开启地域限制开关,模式可选择「仅允许」或「禁止」。
|
||||
* * 例如,若你的服务只服务于国内,可以将模式设为「仅允许」,并在国家列表中勾选 `中国`。
|
||||
* * 支持细化到具体省份/地区(Region),一键拦截特定地理区域的恶意流量。
|
||||
|
||||
#### 3. 人机挑战配置 (PoW CC 防护)
|
||||
* **说明**:开启防 CC 的人机挑战。当请求触发防CC机制时,浏览器会渲染一个静默挑战页面,并在几百毫秒内完成数学计算(哈希碰撞)。通过后会被写入 Cookie,后续访问直接放行。此过程对真实用户几乎无感,但能完美拦截不支持 JS/不具备计算能力的爆破脚本与 CC 僵尸工具。
|
||||
* **核心参数**:
|
||||
* **开启状态**:启用/禁用。
|
||||
* **哈希难度**:控制碰撞难度(建议设定为 `4` 或 `5`)。
|
||||
* **Cookie 有效期**:挑战通过后,在多长时间内免验证(例如 `3600` 秒)。
|
||||
* **自定义挑战 HTML**:可定制挑战中的 Loading 页面风格,让其融入你的业务设计。
|
||||
|
||||
#### 4. 拦截返回 (Block Response)
|
||||
* **说明**:设定 WAF 规则拦截恶意请求时的返回行为。
|
||||
* **配置**:
|
||||
* **拦截状态码**:可自定义拦截响应的 HTTP 状态码,例如标准的 `403`,或带有趣味性质的 `418 (I'm a teapot)`。
|
||||
* **拦截响应体**:可在此输入自定义的 HTML 内容,展示给被拦截的攻击者(如:“WAF 拦截:你的请求已被记录”)。
|
||||
|
||||
---
|
||||
|
||||
### 第三步:将规则组关联到网站
|
||||
|
||||
规则组配置完成后,并不会自动生效,你需要将其与具体的网站配置绑定。
|
||||
|
||||
* **方案 A (推荐)**:在规则组详情页面的 **「绑定网站」** 选项卡中,一键勾选你希望启用此防护的网站并保存。
|
||||
* **方案 B**:回到 **「网站配置」** 中编辑某个具体网站,在其「安全防护」配置区,勾选并绑定刚才创建的规则组。
|
||||
|
||||
> [!NOTE]
|
||||
> 如果规则组被标记为 **「全局规则组 (is_global)」**,它将自动应用到网关上托管的**所有网站**,无需手动执行绑定。
|
||||
|
||||
---
|
||||
|
||||
### 第四步:发布并生效配置
|
||||
|
||||
1. 如果你修改了 **规则组定义**、**GeoIP 范围**、**PoW 防CC难度** 或 **网站的绑定关系**:
|
||||
* 你需要点击管理端右上角的 **「配置预览」** -> **「发布并激活」**。
|
||||
* Agent 拉取并校验新版本后,将重写本地 OpenResty 核心配置文件(`waf_config.json` 等)并平滑重载进程使策略生效。
|
||||
2. 如果你只是更新了 **IP 组的成员名单**(如:在手动 IP 组中删减了一个 IP,或者自动 IP 组定时聚合出了一批新的封禁 IP):
|
||||
* **不需要做任何发布操作!**
|
||||
* Server 会在数据库更新后立即计算 IP 组全新的 Checksum 摘要。
|
||||
* 控制面会通过 **WebSocket 长连接实时向所有在线的 Agent 广播** 变更的 IP 组成员,Agent 接收后会增量覆写到本地的运行时磁盘文件 `waf_ip_groups.json`。
|
||||
* OpenResty Lua 引擎在处理新请求时,会在微秒级计算文件哈希,若发现 Checksum 变更则实时重载入内存字典(`ngx.shared`),**整个过程全程不需要 reload 任何 Nginx 服务,对线上高并发业务毫无影响**。
|
||||
* 即使 WebSocket 连接意外中断,Agent 也会在每周期心跳中上报本地 Checksum,由 Server 差分补齐下发,确保万无一失。
|
||||
|
||||
---
|
||||
|
||||
## WAF 判定逻辑 (过滤漏斗)
|
||||
|
||||
当一个外部请求到达 OpenResty 数据面时,WAF 运行时引擎会以微秒级的极速开销进行如下判决流检测。只要判定出明确结果,即不再向下执行:
|
||||
|
||||
```text
|
||||
请求进入 access 阶段
|
||||
│
|
||||
▼
|
||||
获取当前请求绑定的所有规则组 (全局规则组 + 自定义规则组)
|
||||
│
|
||||
▼
|
||||
1. 匹配 IP 白名单 / 白名单 IP 组? ──────(是)─────► [ 放行 (ALLOW) ]
|
||||
│ (否)
|
||||
▼
|
||||
2. 匹配国家 / 省份地域白名单? ────────(是)─────► [ 放行 (ALLOW) ]
|
||||
│ (否,且已配置任意白名单) ─► [ 拦截 (BLOCK) ]
|
||||
│ (否,且未配置白名单)
|
||||
▼
|
||||
3. 匹配 IP 黑名单 / 黑名单 IP 组? ──────(是)─────► [ 拦截 (BLOCK) ] ──► 返回自定义状态码与HTML拦截页
|
||||
│ (否)
|
||||
▼
|
||||
4. 匹配国家 / 省份地域黑名单? ────────(是)─────► [ 拦截 (BLOCK) ] ──► 返回自定义状态码与HTML拦截页
|
||||
│ (否)
|
||||
▼
|
||||
5. 该站点是否启用了 PoW CC 防护?
|
||||
├───(是)───► [ 校验 PoW Cookie ] ──(验证通过)──► [ 放行 (ALLOW) ]
|
||||
│ │
|
||||
│ (未通过)
|
||||
│ ▼
|
||||
│ [ 渲染 PoW 挑战页 ] ──(计算正确)──► 写入 Cookie 并放行
|
||||
▼
|
||||
6. 未触发任何策略,属于正常业务流量 ───────────────► [ 放行 (ALLOW) ]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践与调优建议
|
||||
|
||||
* **白名单准入语义**:一旦某个生效规则组配置了 IP 白名单、白名单 IP 组或地域白名单,请求必须命中其中至少一条白名单规则才会继续放行;未命中白名单的请求会被拦截。白名单命中后仍会优先绕过后续黑名单与 PoW 检查。
|
||||
* **白名单前置与保护**:在部署高强度黑名单或地域屏蔽前,建议首先创建一个「受信任 IP 组」,放入你团队的办公室出口 IP、本地开发 IP 以及可能访问你的第三方回调源站 IP(如微信、支付宝支付回调地址),并在规则组的**白名单**中优先引入。这可以有效防止误杀,但也会让未在白名单内的来源无法访问。
|
||||
* **合理微调 PoW 难度**:人机 CC 挑战的哈希碰撞计算(`challenge_difficulty`)是一把双刃剑。
|
||||
* 难度值 `3`:几乎瞬间完成计算,防 CC 强度低。
|
||||
* 难度值 `4`:普通手机/低端浏览器在 100~300ms 内完成计算,防护性能良好。
|
||||
* 难度值 `5`:需要 500ms~2s,防护性强,但低配端可能会感觉稍显卡顿。
|
||||
* 难度值 `6` 及以上:计算量呈指数级上升,可能导致移动端用户浏览器 CPU 持续打满卡死。**因此强烈建议在生产环境选用 `4` 或 `5`**。
|
||||
* **善用“测试规则”**:对于自动 IP 组,在点击保存之前务必点击 **「测试规则」**。通过分析当前窗口内被命中的 IP 列表,确认你的 Expr 表达式阈值(如请求数、404占比等)配置是否过宽或过紧,防止由于阈值配置不合理导致大面积误封正常用户。
|
||||
* **分离静态与动态黑名单**:不要将需要长期封禁的静态恶意 IP 填入自动封禁组(因为自动聚合的名单随时会被新的执行窗口覆盖)。应该将确定的恶意 IP 录入到一个专门的「手动封禁 IP 组」中,并让规则组同时引用该手动组与自动组。
|
||||
@@ -0,0 +1,188 @@
|
||||
# 开发约束
|
||||
|
||||
OpenFlare 代码修改的准入标准、后端/Agent/前端分层约束、数据模型边界、API 约定、数据库迁移要求和测试交付基线。
|
||||
|
||||
## 变更准入
|
||||
|
||||
新需求进入实现前,按以下顺序判断:
|
||||
|
||||
1. 是否符合 [产品边界](../design/index.md)。
|
||||
2. 是否符合本文档的后端、Agent 与前端约束。
|
||||
3. 是否会破坏现有发布、同步、回滚或升级主链路。
|
||||
4. 是否需要同步更新部署、配置、README 或文档站页面。
|
||||
|
||||
如果需求超出边界或引入新基础设施,应先更新设计文档,再开始实现。
|
||||
|
||||
## 技术基线
|
||||
|
||||
Server:
|
||||
|
||||
* Go 1.25+
|
||||
* Gin
|
||||
* GORM
|
||||
* SQLite / PostgreSQL
|
||||
* 现有登录体系
|
||||
|
||||
Agent:
|
||||
|
||||
* 单二进制
|
||||
* 节点本地执行
|
||||
* 通过 `openresty_path` 或默认 `openresty` 控制 OpenResty 二进制
|
||||
* Docker 部署使用内置 OpenResty 的 Agent 镜像,不由 Agent 再控制独立 OpenResty 容器
|
||||
|
||||
Frontend:
|
||||
|
||||
* Next.js 15 App Router
|
||||
* React 19
|
||||
* TypeScript 5
|
||||
* Tailwind CSS 4
|
||||
* TanStack Query
|
||||
* React Hook Form + Zod
|
||||
* Zustand 仅用于轻量客户端状态
|
||||
* ESLint + Prettier
|
||||
* Vitest + Testing Library + Playwright
|
||||
* pnpm
|
||||
|
||||
## 工程分层约束
|
||||
|
||||
各组件和模块(Server、Agent、Frontend)的物理目录分层职责详见 [仓库结构](../design/index.md#仓库结构)。在此结构下,开发必须遵守以下核心分层规则:
|
||||
|
||||
* **Server 开发规则**:
|
||||
* 禁止在 `controller/` 堆积业务逻辑,禁止在 `middleware/` 实现业务流程,禁止为简单需求新增平台层抽象。
|
||||
* **定时任务开发规则**:禁止将不同业务模块(如 Uptime Kuma 整合、WAF IP 同步等)的定时任务具体执行逻辑与状态堆积在单个 `cron.go` 文件中。各模块对应的定时任务结构体和运行逻辑必须在独立的 Go 文件中定义,`cron.go` 只允许承担统一注册、初始化与调度器启停的职责。
|
||||
* **Agent 开发规则**:每个模块职责单一,外部命令调用集中封装,状态落盘与配置落盘分离。
|
||||
* **Frontend 开发规则**:页面文件只负责获取路由参数、组织页面结构、调用 feature 组件;不应手写复杂 API 细节、复杂表单校验逻辑或维护大量彼此耦合的局部状态。
|
||||
|
||||
## 数据模型规范
|
||||
|
||||
在定义和修改 Go/GORM 模型实体时,所有模型的业务边界与设计约束必须严格符合 [产品边界](../design/index.md)。
|
||||
|
||||
### 1. 当前有效实体
|
||||
* **核心配置与反代**:`proxy_routes` (网站配置), `origins` (源站), `config_versions` (配置版本), `tls_certificates` (证书), `managed_domains` (托管域名).
|
||||
* **Pages 静态托管**:`pages_projects` (Pages 项目), `pages_deployments` (不可变部署), `pages_deployment_files` (部署文件清单).
|
||||
* **节点与状态**:`nodes` (节点), `node_system_profiles` (系统概况), `apply_logs` (应用日志).
|
||||
* **内网穿透**:`tunnels` (隧道客户端), `tunnel_tokens` (隧道认证令牌,可选持久化).
|
||||
* **观测与分析**:`node_request_reports` (请求上报), `node_access_logs` (访问明细), `node_metric_snapshots` (指标快照), `traffic_analytics_rollups` (流量聚合), `node_health_events` (健康事件).
|
||||
* **系统配置与第三方登录**:`options` (全局参数), `auth_sources` (第三方认证源), `external_accounts` (外部绑定账号).
|
||||
* **安全与 WAF**:`waf_rule_groups` (WAF规则组), `waf_ip_groups` (WAF IP组), `waf_rule_group_bindings` (网站WAF绑定).
|
||||
|
||||
### 2. 底层数据库技术约束
|
||||
|
||||
在编写或修改模型时,必须严格遵守以下持久化与数据库设计准则:
|
||||
|
||||
* **禁止随意引入平台化新实体**:除非 [产品边界](../design/index.md) 设计发生调整并经评审。
|
||||
|
||||
## 数据库迁移
|
||||
|
||||
任何涉及表结构、索引、列类型、分表规则或内部持久化元数据的修改,都必须同步提升数据库版本号。
|
||||
|
||||
数据库版本号定义在 `openflare-server/model`,不得只依赖 `AutoMigrate` 隐式升级存量数据库。
|
||||
|
||||
每次提升数据库版本号时,必须补充从上一版本升级到新版本的显式迁移方法。迁移方法必须包含升级后的校验逻辑;只有校验通过,才能写入新的数据库版本记录。
|
||||
|
||||
数据库升级统一使用 goose。新的 goose provider、桥接逻辑、注册入口和具体迁移文件必须全部放在 `openflare-server/model/goose` 包下,`openflare-server/model` 根包只保留纯净实体类、旧框架兼容适配和必要的上下文注入。每次新增数据库升级都必须新建一个单独的 Go 文件,文件名使用 `openflare-server/model/goose/goose_<timestamp>_<description>.go`,例如 `openflare-server/model/goose/goose_202606020001_add_node_capabilities_json.go`。迁移文件必须同时包含该版本的 goose migration 构造函数、升级逻辑和校验逻辑;`model/goose/migrations.go` 只能作为注册入口和公共构造工具,禁止把具体迁移逻辑集中堆放在该文件中。
|
||||
|
||||
执行数据库升级时必须按以下步骤完成:
|
||||
|
||||
1. 判断是否需要升级数据库版本:凡是新增/删除/重命名表、字段、索引、约束、列类型、分表规则,或改变持久化数据语义,都必须升级。
|
||||
2. 新增 `openflare-server/model/goose/goose_<timestamp>_<description>.go`,其中 `<timestamp>` 为 goose 版本号。文件头部或迁移构造函数附近必须包含注释,说明本次升级了什么内容,以及为什么需要升级。
|
||||
3. 在该文件中实现独立迁移构造函数,并返回通过 `newGORMMigration(...)` 创建的 migration;随后只在 `openflare-server/model/goose/migrations.go` 的 `registeredMigrations(...)` 中新增一条注册项。
|
||||
4. 在同一个单独迁移文件中写入升级逻辑。可通过 goose `Context` 调用 `ApplyCurrentSchema`、历史 backfill、默认数据初始化等公共能力;复杂数据修复必须显式处理,不得只依赖 `AutoMigrate`。
|
||||
5. 在同一个单独迁移文件中写入升级后的校验逻辑。校验至少要覆盖新增表/字段/索引是否存在、关键默认数据是否存在、必要的数据回填是否成功。
|
||||
6. 如果新迁移需要新的公共 backfill 或校验辅助函数,优先放在该迁移文件中;只有多个迁移共同复用时,才放到 `openflare-server/model/goose` 包内的公共文件中。不要把新 goose 框架代码放回 `openflare-server/model` 根包。
|
||||
7. 补充迁移测试:至少覆盖从旧框架终点或上一 goose 版本升级后 schema version、字段/表结构、关键数据回填和校验结果。还应保留旧库从 v15/v17 桥接到 goose 的回归覆盖。
|
||||
|
||||
新包启动后必须先检查数据库当前版本,再按顺序逐步升级到目标版本;禁止跳过中间升级步骤直接写目标版本。
|
||||
|
||||
空库初始化可以直接建立当前版本结构,但初始化完成后仍必须执行同版本校验,并落库当前数据库版本。
|
||||
|
||||
如果迁移失败或校验失败,启动流程必须中止,确保数据库能够回滚。涉及数据库版本变更的提交,必须补充对应的迁移测试或等效回归测试。
|
||||
|
||||
## API 与鉴权
|
||||
|
||||
管理端与 Agent/Relay/Client API 统一使用 JSON。成功与失败都必须返回清晰 `message`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
约定:
|
||||
|
||||
* Agent API 固定放在 `/api/agent/*`,使用 `X-Agent-Token` 认证(节点专属 token)。
|
||||
* **Relay API** 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 认证(同 TunnelRelay 节点)。
|
||||
- Server 通过 token + `/api/relay/*` 路径区分 Relay 请求。
|
||||
* **Tunnel Client API** 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 认证(独立的 tunnel_token)。
|
||||
- OpenFlared 使用 `tunnel_token` 与 Server 通信,独立于 Agent 认证体系。
|
||||
* **Admin Tunnel 管理 API** - `/api/tunnels/*`。
|
||||
- CRUD tunnel 实体(创建、查询、更新、删除)。
|
||||
- Token 管理(生成、轮换)。
|
||||
- 强制同步(触发 Client 立即拉取新配置)。
|
||||
* **Admin Pages 管理 API** - `/api/pages/*`。
|
||||
- CRUD Pages 项目,包括 SPA fallback 启用状态与回退路径。
|
||||
- 上传 zip 部署包、查看部署历史、激活部署、删除非激活部署。
|
||||
* **Agent Pages 下载 API** - `/api/agent/pages/*`,使用 `X-Agent-Token` 认证。
|
||||
- Agent 仅能按激活配置引用的部署 ID 拉取静态部署包,不提供任意文件读取或远程命令入口。
|
||||
* 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。
|
||||
* 管理端登录成功后返回用户 token;管理端 API 只允许从 `OPENFLARE_TOKEN` 请求头读取登录凭证,不得通过 Cookie Session 放行。
|
||||
* `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。
|
||||
* 系统仅单租户使用, 不得创建用户。
|
||||
* Agent/Relay/Client 正式请求统一使用对应的专属 token(`agent_token` / `relay_token`(即 agent_token) / `tunnel_token`)。
|
||||
* 首次接入 Agent 可使用全局 `discovery_token`;首次接入 Client 由 Server 生成 tunnel_token,直接用于部署命令。
|
||||
* Agent/Relay 请求头统一使用 `X-Agent-Token`;Client 请求头统一使用 `X-Tunnel-Token`。
|
||||
|
||||
## 前端请求、状态与类型
|
||||
|
||||
所有 API 请求必须统一经过 `lib/api/`:
|
||||
|
||||
* 统一处理 `success/message/data` 响应结构。
|
||||
* 统一处理鉴权失效、网络异常和通用错误消息。
|
||||
* 统一维护资源接口与请求路径。
|
||||
|
||||
状态分层:
|
||||
|
||||
* 服务端状态:TanStack Query。
|
||||
* 页面临时状态:组件内部 `useState`。
|
||||
* 跨页面 UI 状态:Zustand。
|
||||
|
||||
要求开启 TypeScript 严格模式,禁止滥用 `any`,API 响应、表单输入、业务实体必须有明确类型。
|
||||
|
||||
## 表单、交互、样式与主题
|
||||
|
||||
表单统一使用 React Hook Form 与 Zod。
|
||||
|
||||
高风险操作必须二次确认、展示操作对象名称,并明确成功与失败反馈。
|
||||
|
||||
样式原则:
|
||||
|
||||
* 统一使用 Tailwind CSS 与现有 token 体系。
|
||||
* 优先复用已有基础组件与布局组件。
|
||||
* 保持视觉层级、留白与语义颜色一致。
|
||||
|
||||
主题要求:
|
||||
|
||||
* 同时支持 `light`、`dark`、`system`。
|
||||
* 用户选择必须持久化。
|
||||
* 首屏尽量避免主题闪烁。
|
||||
|
||||
## 测试与交付
|
||||
|
||||
* 关键业务逻辑必须有单元测试或等效回归测试。
|
||||
* Agent 主链路修改必须验证同步、应用与回滚。
|
||||
* 前端页面至少覆盖加载态、空态、错误态与成功反馈。
|
||||
* Go 版本调整时,同步检查 `go.mod`、Dockerfile 与 CI 工作流。
|
||||
|
||||
## 后续维护方式
|
||||
|
||||
后续规划不再按“大版本阶段文档”维护,而采用以下方式:
|
||||
|
||||
* 产品边界变动:更新 [产品边界](../design/index.md)。
|
||||
* 工程约束变动:更新本文档。
|
||||
* 部署与配置变动:更新 [部署说明](../deployment/deployment.md)、[配置项](../reference/configuration.md) 与 README。
|
||||
|
||||
如果未来出现明确的新阶段目标,再单独新增专项计划文档;不要把已完成的历史计划继续堆回本文档。
|
||||
|
||||
当前专项“网站级规则与配置界面改造”的模型边界已纳入 [产品边界](../design/index.md),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。
|
||||
@@ -0,0 +1,189 @@
|
||||
你是一个资深 Go 后端工程师,负责维护和开发一个长期演进的 Go 应用。
|
||||
|
||||
你的目标不是“尽快写完代码”,而是产出可维护、可测试、可演进、符合 Go 生态习惯的高质量代码。禁止为了完成任务而堆砌临时代码、过度抽象、重复逻辑或破坏现有架构。
|
||||
|
||||
在任何开发前,你必须先阅读并理解现有代码结构,包括:
|
||||
- 项目目录结构
|
||||
- 入口文件
|
||||
- 配置管理方式
|
||||
- 数据库/缓存/消息队列访问方式
|
||||
- HTTP/RPC/API 层设计
|
||||
- service/usecase/domain/repository 等分层方式
|
||||
- 错误处理方式
|
||||
- 日志方式
|
||||
- 测试组织方式
|
||||
- 依赖注入方式
|
||||
|
||||
如果你不确定某个模块的职责,先通过代码上下文推断,不要随意新建重复模块。
|
||||
|
||||
开发原则:
|
||||
|
||||
1. 架构优先
|
||||
- 优先融入现有架构,而不是另起炉灶。
|
||||
- 不要随便新增 global variable、init 副作用、隐式依赖。
|
||||
- 不要把业务逻辑写进 handler/controller。
|
||||
- handler 只负责参数解析、鉴权上下文、调用 usecase/service、返回响应。
|
||||
- service/usecase 负责业务编排。
|
||||
- repository/dao 负责数据访问。
|
||||
- domain/model 负责核心业务对象和规则。
|
||||
- 基础设施代码与业务代码隔离。
|
||||
|
||||
2. Go 风格
|
||||
- 使用清晰、直接、朴素的 Go 代码。
|
||||
- 不要模仿 Java 式过度抽象。
|
||||
- interface 应该由使用方定义,而不是提供方强行定义。
|
||||
- 小接口优先。
|
||||
- 命名要准确,不使用 Manager、Helper、Util 这类含糊名称,除非确实必要。
|
||||
- 函数保持短小,单一职责。
|
||||
- 不要为了“看起来高级”引入泛型、反射、复杂设计模式。
|
||||
- 不要隐藏错误。
|
||||
- error 必须带上下文信息,必要时使用 fmt.Errorf("...: %w", err)。
|
||||
- 不要 panic,除非是程序启动阶段的不可恢复错误。
|
||||
|
||||
3. 可维护性
|
||||
- 修改前先分析影响范围。
|
||||
- 不改变公开 API、数据库结构、配置格式,除非任务明确要求。
|
||||
- 如果必须改变,要说明兼容性影响和迁移方案。
|
||||
- 删除代码前确认没有调用方。
|
||||
- 避免复制粘贴已有逻辑,应抽取到合适位置,但不要过度抽象。
|
||||
- 对复杂业务逻辑添加必要注释,解释“为什么”,不要注释显而易见的“是什么”。
|
||||
- 开发前先检查 utils、helpers 包,避免重复造轮子。
|
||||
|
||||
4. 测试要求
|
||||
- 新增业务逻辑必须补充单元测试。
|
||||
- 修复 bug 必须补充回归测试。
|
||||
- 测试应覆盖正常路径、异常路径、边界条件。
|
||||
- 不要为了测试方便破坏业务代码结构。
|
||||
- 外部依赖使用 mock/fake/stub 隔离。
|
||||
- 测试命名清晰,例如 TestXXX_WhenYYY_ShouldZZZ。
|
||||
- 表驱动测试优先,但不要为了表驱动牺牲可读性。
|
||||
|
||||
5. 并发与资源管理
|
||||
- goroutine 必须有退出机制。
|
||||
- 涉及 context 的地方必须正确传递 context.Context。
|
||||
- 不要随意使用 context.Background() 替代上游 context。
|
||||
- channel 必须明确关闭责任。
|
||||
- 锁的范围要小,避免死锁。
|
||||
- HTTP、数据库、文件、连接等资源必须正确关闭。
|
||||
- 注意 race condition、goroutine leak、连接泄露。
|
||||
|
||||
6. 数据库与事务
|
||||
- 数据库访问必须在 repository/dao 层。
|
||||
- 事务边界应由业务用例层控制,而不是散落在多个底层函数中。
|
||||
- 不要在循环中产生明显低效的 N+1 查询,除非数据量可控且有说明。
|
||||
- SQL 要可读、参数化,禁止拼接不可信输入。
|
||||
- schema 变更必须考虑迁移、回滚和兼容性。
|
||||
|
||||
7. API 设计
|
||||
- 请求参数必须校验。
|
||||
- 错误响应要稳定、清晰,不泄露内部敏感信息。
|
||||
- 日志中不要打印密码、token、密钥、身份证号等敏感数据。
|
||||
- 返回结构保持向后兼容。
|
||||
- HTTP 状态码要语义正确。
|
||||
- API 返回要有一致的格式,例如 { "code": 0, "message": "success", "data": {...} }。
|
||||
- API 返回统一使用封装的方法 response.go,不要直接构造响应。
|
||||
|
||||
8. 日志与可观测性
|
||||
- 关键路径要有必要日志。
|
||||
- 错误日志要包含排查所需上下文,但不要泄露敏感数据。
|
||||
- 不要滥打日志。
|
||||
- 不要在库代码里直接 fmt.Println。
|
||||
- 如果项目已有 logger,要统一使用现有 logger。
|
||||
|
||||
9. 安全要求
|
||||
- 所有外部输入都不可信。
|
||||
- 不要硬编码密钥、token、密码。
|
||||
- 不要把敏感配置提交到代码。
|
||||
- 文件路径、URL、命令执行、SQL、模板渲染等位置必须注意注入风险。
|
||||
- 鉴权和权限判断必须放在明确的位置,不能依赖前端或调用方自觉。
|
||||
|
||||
10. 性能要求
|
||||
- 不要过早优化。
|
||||
- 但不能写明显低效代码。
|
||||
- 对热点路径要避免不必要的内存分配、大对象复制、重复解析。
|
||||
- 大数据量处理应考虑分页、流式处理、批量操作。
|
||||
- 如果引入缓存,必须说明一致性、过期策略和失效条件。
|
||||
|
||||
工作流程:
|
||||
|
||||
每次接到开发任务,你必须按以下步骤执行:
|
||||
|
||||
第一步:理解需求
|
||||
- 用自己的话简要复述需求。
|
||||
- 明确输入、输出、边界条件、异常情况。
|
||||
- 如果需求含糊,列出你的合理假设,不要直接乱写。
|
||||
|
||||
第二步:阅读现有代码
|
||||
- 找出相关模块、调用链、数据结构、接口、测试。
|
||||
- 说明当前代码是如何工作的。
|
||||
- 判断改动应该放在哪一层。
|
||||
|
||||
第三步:设计方案
|
||||
- 给出最小可行修改方案。
|
||||
- 说明为什么放在这些文件/模块中。
|
||||
- 说明是否影响已有 API、数据库、配置、测试。
|
||||
- 如果有多个方案,比较优缺点,选择更稳妥的方案。
|
||||
|
||||
第四步:编码
|
||||
- 只修改与任务相关的代码。
|
||||
- 保持现有代码风格。
|
||||
- 不引入不必要的新依赖。
|
||||
- 不制造重复逻辑。
|
||||
- 不留下 TODO、临时代码、调试代码。
|
||||
|
||||
第五步:测试
|
||||
- 补充或更新测试。
|
||||
- 说明测试覆盖了哪些场景。
|
||||
- 如果无法运行测试,要说明原因,并给出应该运行的命令。
|
||||
|
||||
第六步:交付说明
|
||||
- 总结改了什么。
|
||||
- 说明为什么这样改。
|
||||
- 说明潜在风险。
|
||||
- 给出验证方式。
|
||||
- 如果存在未完成项,必须明确列出,不要假装完成。
|
||||
|
||||
输出格式:
|
||||
|
||||
你每次回复都应包含:
|
||||
|
||||
1. 需求理解
|
||||
2. 现有代码分析
|
||||
3. 修改方案
|
||||
4. 具体改动
|
||||
5. 测试与验证
|
||||
6. 风险与注意事项
|
||||
|
||||
如果只是让我审查代码,则输出:
|
||||
1. 问题列表
|
||||
2. 严重程度:致命 / 高 / 中 / 低
|
||||
3. 影响说明
|
||||
4. 修改建议
|
||||
5. 推荐改法示例
|
||||
|
||||
代码质量红线:
|
||||
|
||||
禁止出现以下行为:
|
||||
- 为了完成需求复制粘贴大段重复代码
|
||||
- 在 handler 中塞业务逻辑
|
||||
- 到处传 map[string]interface{}
|
||||
- 使用全局变量绕过依赖注入
|
||||
- 随意新增 util/helper 垃圾桶包
|
||||
- 忽略 error
|
||||
- catch-all 式错误处理
|
||||
- 函数超过合理长度仍继续堆逻辑
|
||||
- 修改无关代码
|
||||
- 未经说明改变已有行为
|
||||
- 无测试地修改核心逻辑
|
||||
- 引入大型依赖只为解决小问题
|
||||
- 写完代码不说明验证方式
|
||||
- 不理解现有架构就直接重构
|
||||
|
||||
当你发现现有代码已经比较混乱时:
|
||||
- 不要一次性大重构。
|
||||
- 先局部止血。
|
||||
- 新代码尽量写在清晰边界内。
|
||||
- 对旧代码只做必要改动。
|
||||
- 如果需要重构,先提出分阶段计划。
|
||||
|
||||
请始终以“长期维护这个项目的人”的标准来写代码,而不是以“完成一次性任务”的标准来写代码。
|
||||
+20
-14
@@ -3,8 +3,8 @@ layout: home
|
||||
|
||||
hero:
|
||||
name: OpenFlare
|
||||
text: 自托管 OpenResty 控制面
|
||||
tagline: 管理反向代理规则、配置发布、节点同步、TLS 证书与基础观测。
|
||||
text: 开源 CDN 编排与边缘安全平台
|
||||
tagline: 支持反向代理、集中式配置同步、Pages 静态托管、内网穿透(Tunnels)、动态 WAF 防护与人机防 CC 挑战。
|
||||
actions:
|
||||
- theme: brand
|
||||
text: 快速开始
|
||||
@@ -17,16 +17,22 @@ hero:
|
||||
link: https://github.com/Rain-kl/OpenFlare
|
||||
|
||||
features:
|
||||
- icon: 🧭
|
||||
title: 统一控制面
|
||||
details: 在一个管理端维护网站、域名、源站、证书、节点与版本状态。
|
||||
- icon: 🚀
|
||||
title: 不可变发布
|
||||
details: 每次发布生成完整 OpenResty 配置快照,可预览、激活和回滚。
|
||||
- icon: 🔁
|
||||
title: Agent 自动应用
|
||||
details: 节点侧自动拉取、校验、reload,并在失败时回滚到可运行配置。
|
||||
- icon: 📊
|
||||
title: 基础观测
|
||||
details: 提供请求聚合、访问分析、资源快照、健康事件与节点详情。
|
||||
- icon: 🛰️
|
||||
title: 集中式配置同步
|
||||
details: 通过 WebSocket 与心跳实现全网节点配置秒级同步下发与热生效,状态即时回收。
|
||||
- icon: 🌐
|
||||
title: 分布式 CDN 编排
|
||||
details: 将独立的 OpenResty 编排为高度协同的分布式 CDN 舰队,支持源站多负载均衡。
|
||||
- icon: 📄
|
||||
title: Pages 静态托管
|
||||
details: 直接上传前端打包 zip 资产,由边缘节点拉取解压并提供高性能本地服务与 API 代理。
|
||||
- icon: 🚇
|
||||
title: 安全内网穿透 (Tunnels)
|
||||
details: 对标 Cloudflare Tunnels,无须公网 IP 或暴露入向端口,安全穿透本地服务至公网。
|
||||
- icon: 🛡️
|
||||
title: 边缘 WAF 安全防护
|
||||
details: IP 组成员差分同步写入 Lua 共享内存,实现免 Nginx 重载的 WAF 热更新与 GeoIP 过滤。
|
||||
- icon: 🧩
|
||||
title: 防 CC 与人机挑战 (PoW)
|
||||
details: 内置高性能客户端 Proof of Work 密码学挑战,网关边缘秒级拦截阻断僵尸网络与爬虫。
|
||||
---
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# 交互式智能安装脚本实现计划
|
||||
|
||||
本计划旨在拓展 OpenFlare Agent 安装脚本的功能,支持交互式选择本地安装或 Docker 容器安装,并提供智能检测/在线安装 Docker 环境的机制,同时保留通过命令行传参进行自动化安装的既有能力。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与背景 (Goal & Context)
|
||||
* **需求背景**:当前安装脚本 `install-agent.sh` 仅支持在宿主机直接下载二进制并配置为本地 systemd 服务运行。随着 Docker 部署方式的普及,需要让用户在一键安装时能根据需要交互式选择 Docker 或本地部署,从而提升部署体验。
|
||||
* **开发范围 (Scope)**:
|
||||
* 支持交互式运行(未传参时):提示用户选择 Local 方式或 Docker 方式。
|
||||
* 当选择 Docker 方式时,检测本地是否存在 `docker` 命令。若不存在,提示并在线安装 Docker(支持国内镜像源及自动测速选择最低延迟源)。
|
||||
* 交互引导用户配置 `server_url` 和 `agent-token` 或 `discovery-token`。
|
||||
* 如果选择 Docker,则最终拉取 Agent 镜像并运行容器;如果选择 Local,则继续原有的本地二进制下载及配置发布逻辑。
|
||||
* 兼容非交互式模式:如果执行脚本时传递了任意参数,则跳过任何交互式提示,直接进行自动化安装(支持新参数 `--docker` / `--method docker` 来自动选用 Docker 部署)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计与决策 (Design & Decisions)
|
||||
|
||||
### 交互工作流
|
||||
1. 检查 `$#`(参数数量)。若 `$# -eq 0`,激活 `INTERACTIVE=true`。
|
||||
2. 在交互模式下:
|
||||
* 引导用户选择安装方法(1: Local, 2: Docker)。
|
||||
* 若选择 Docker,调用 `Install_Docker` 检测并安装环境。
|
||||
* 引导用户输入 `SERVER_URL` 并进行非空校验。
|
||||
* 引导用户选择 Token 类型(1: Discovery Token, 2: Agent Token),并输入对应的 Token 值。
|
||||
* 若选择 Local 且未传 `--openresty-path`,如果 `openresty` 二进制未能在 $PATH 中找到,交互提示用户手动输入 OpenResty 路径。
|
||||
3. 非交互模式下:
|
||||
* 解析命令行参数。
|
||||
* 支持通过 `--docker` 或 `--method docker` 指定 Docker 容器安装。
|
||||
* 依然根据传入的 `--server-url` 和 Token 自动执行安装,绝不进行任何交互。
|
||||
|
||||
### 数据流与架构图
|
||||
```mermaid
|
||||
graph TD
|
||||
Start[执行 install-agent.sh] --> CheckArgs{是否有命令行参数?}
|
||||
|
||||
CheckArgs -- 是 (非交互模式) --> ParseArgs[解析参数]
|
||||
ParseArgs --> IsDockerParam{是否指定 Docker?}
|
||||
IsDockerParam -- 是 --> RunDocker[Docker 容器拉取与启动]
|
||||
IsDockerParam -- 否 --> RunLocal[本地二进制下载与 systemd 服务创建]
|
||||
|
||||
CheckArgs -- 否 (交互模式) --> PromptMethod[提示选择 Local 或 Docker]
|
||||
PromptMethod --> MethodChosen{选择结果}
|
||||
|
||||
MethodChosen -- Docker --> CheckDocker{本地有 Docker 吗?}
|
||||
CheckDocker -- 否 --> PromptDockerInstall[询问是否安装 Docker?]
|
||||
PromptDockerInstall -- 是 --> InstallDocker[在线安装 Docker + 配置国内镜像加速]
|
||||
PromptDockerInstall -- 否 --> ExitScript[取消安装并退出]
|
||||
CheckDocker -- 是 --> PromptServerUrl[提示输入 Server URL & Token]
|
||||
InstallDocker --> PromptServerUrl
|
||||
|
||||
MethodChosen -- Local --> PromptLocalConfig[检测 OpenResty 并提示输入 Server URL & Token]
|
||||
|
||||
PromptServerUrl --> RunDocker
|
||||
PromptLocalConfig --> RunLocal
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 具体修改文件清单 (Proposed Changes)
|
||||
|
||||
### 边缘 Agent 与部署脚本
|
||||
* #### [MODIFY] [install-agent.sh](file:///Users/ryan/DEV/Go/OpenFlare/scripts/install-agent.sh)
|
||||
* 职责:
|
||||
1. 引入交互式选择逻辑和 `Install_Docker` / `configure_accelerator` 函数。
|
||||
2. 新增 `--docker` 和 `--method` 命令行参数支持。
|
||||
3. 支持用户交互输入配置项。
|
||||
4. 增加 Docker 镜像拉取、停止旧容器并启动新容器的安装路径。
|
||||
* #### [MODIFY] [agent.md](file:///Users/ryan/DEV/Go/OpenFlare/docs/deployment/agent.md)
|
||||
* 职责:更新一键安装说明文档,增加交互式模式的说明以及 `--docker` 参数的自动化 Docker 安装说明。
|
||||
|
||||
---
|
||||
|
||||
## 4. 验证计划 (Verification Plan)
|
||||
|
||||
### 自动化与脚本测试
|
||||
* 在干净的测试环境运行脚本:
|
||||
* `bash scripts/install-agent.sh` (测试交互模式 -> 选用 Local)
|
||||
* `bash scripts/install-agent.sh` (测试交互模式 -> 选用 Docker)
|
||||
* `bash scripts/install-agent.sh --server-url http://127.0.0.1:3000 --discovery-token mytoken` (测试自动 Local 安装)
|
||||
* `bash scripts/install-agent.sh --server-url http://127.0.0.1:3000 --discovery-token mytoken --docker` (测试自动 Docker 安装)
|
||||
|
||||
### 数据面生效验证
|
||||
* 通过 `docker ps` 和 `docker logs openflare-agent` 确认容器成功拉取并启动,环境参数注入正确。
|
||||
@@ -0,0 +1,92 @@
|
||||
# 登录集成 Cap 验证码实现计划
|
||||
|
||||
本计划规定了在 OpenFlare 系统的登录流程中集成 Cap(基于 Proof-of-Work 和无感浏览器检测的验证码)的具体开发步骤。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与背景 (Goal & Context)
|
||||
|
||||
### 需求背景
|
||||
为解决安全分析中识别到的“登录接口缺少防暴力破解/撞库逻辑”这一安全风险,我们需要在登录接口中集成 Cap 验证码服务。通过让客户端(爬虫/浏览器)在登录前必须求解一个 PoW 工作量难题并核销,显著提高恶意爬虫爆破的计算成本,从根本上防止针对登录接口的恶意爆破。
|
||||
|
||||
### 开发范围
|
||||
1. **后端验证服务**:在 Server 端移植 `capjs-core` 的 PoW 校验算法(包括 FNV-1a、自定义 PRNG、SHA-256 检验、JWT 难题派发与核销缓存组件)。
|
||||
2. **公开路由映射**:
|
||||
* `POST /api/cap/challenge` (分发难题)
|
||||
* `POST /api/cap/redeem` (核销难题并核发 `cap-token`)
|
||||
3. **控制开关**:增加全局选项 `CapLoginEnabled`,管理员可动态启停。
|
||||
4. **前端交互接入**:在登录页面引入 `cap-widget` 自定义组件,并在提交登录请求时附带 `cap_token`。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计与决策 (Design & Decisions)
|
||||
|
||||
### 核心对象与数据模型
|
||||
本方案不涉及复杂数据库结构重构,但需要:
|
||||
1. 在 `options` 表中保存 `CapLoginEnabled` (true/false) 选项, 默认为 True, 设置路径在 设置->系统设置->登录与注册开关。
|
||||
2. 建立一个全局的、线程安全的内存验证码核销存储/核销缓存,具备过期清理功能,用于存放核销的 `cap-token` 以及消费过的 JWT 难题 Nonce(Signature),支持 Redis 与本地内存模式。
|
||||
|
||||
### API 与鉴权设计
|
||||
1. **`POST /api/cap/challenge`**:公开接口。
|
||||
2. **`POST /api/cap/redeem`**:公开接口。
|
||||
3. **`POST /api/user/login`**:接受可选/必选的 `cap_token` 参数。
|
||||
|
||||
### 算法移植 (Proof-of-Work Go 实现)
|
||||
* FNV-1a 状态机复现。
|
||||
* 伪随机数生成器 (PRNG) 与 `strings.HasPrefix(sha256Hex, target)` 校验。
|
||||
|
||||
---
|
||||
|
||||
## 3. 具体修改文件清单 (Proposed Changes)
|
||||
|
||||
### 后端 Server
|
||||
* #### [MODIFY] [constants.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/common/constants.go)
|
||||
* 增加 `CapLoginEnabled` 全局常量/变量,默认 `true`。
|
||||
* #### [MODIFY] [option.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/model/option.go)
|
||||
* 在 `InitOptionMap` 和 `updateOptionMap` 中添加 `CapLoginEnabled` 的支持。
|
||||
* #### [NEW] [prng.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/prng.go)
|
||||
* 职责:实现 FNV-1a、FNV-1a resume 及 XORShift-based 自定义 PRNG 伪随机数算法。
|
||||
* #### [NEW] [cap.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/cap.go)
|
||||
* 职责:实现无状态 PoW 难题生成、验证及 JWT 校验。
|
||||
* #### [NEW] [store.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/store.go)
|
||||
* 职责:定义 `Store` 接口并提供默认的高性能、线程安全的内存 TTL 缓存核销存储实现。
|
||||
* #### [NEW] [manager.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/manager.go)
|
||||
* 职责:封装验证码的核心逻辑,暴露出 `Generate`、`Redeem` 与 `VerifyToken` 高阶 API。
|
||||
* #### [NEW] [middleware.go (utils/cap)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/utils/cap/middleware.go)
|
||||
* 职责:实现通用的 Gin 中间件 `VerifyMiddleware`。其不依赖任何 OpenFlare 业务代码,完全通过构造注入。
|
||||
* #### [NEW] [cap.go (service)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/service/cap.go)
|
||||
* 职责:适配器服务,将 OpenFlare 的全局参数(如 `JWTSecret`、`CapLoginEnabled`、`RDB`)注入并实例化全局的 `CapManager` 实例。
|
||||
* #### [NEW] [cap.go (middleware)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/middleware/cap.go)
|
||||
* 职责:极简的适配器中间件,直接调用并返回 `service.CapManager.VerifyMiddleware(scope)`。
|
||||
* #### [NEW] [cap.go (controller)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/controller/cap.go)
|
||||
* 职责:实现 `GetCapChallenge` 和 `RedeemCapChallenge` 控制器。
|
||||
* #### [MODIFY] [api-router.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/router/api-router.go)
|
||||
* 职责:挂载 `/api/cap/challenge` 和 `/api/cap/redeem` 路由,并在 `/api/user/login` 上应用 `middleware.CapAuth("login")`。
|
||||
* #### [MODIFY] [user.go (controller)](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/controller/user.go)
|
||||
* 无需修改:登录控制器和入参结构体保持完全无侵入。
|
||||
* #### [MODIFY] [misc.go](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/controller/misc.go)
|
||||
* 职责:在 `GetStatus` 中返回 `cap_login_enabled` 开关状态。
|
||||
|
||||
### 前端 Web
|
||||
* #### [MODIFY] [public-status.ts](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/web/types/public-status.ts)
|
||||
* 添加 `cap_login_enabled: boolean` 字段。
|
||||
* #### [MODIFY] [auth.ts](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/web/types/auth.ts)
|
||||
* 在 `LoginPayload` 中添加可选的 `cap_token?: string` 属性。
|
||||
* #### [MODIFY] [login-form.tsx](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/web/features/auth/components/login-form.tsx)
|
||||
* 动态载入 `cap-widget`(脚本 CDN:`https://cdn.jsdelivr.net/npm/cap-widget`)。
|
||||
* 若后台返回 `cap_login_enabled === true`,则渲染 `<cap-widget data-cap-api-endpoint="/api/cap/" />` 组件。
|
||||
* 在表单提交时,将 `cap-token` 塞入 `loginMutation` 的 Payload 中提交。
|
||||
|
||||
---
|
||||
|
||||
## 4. 验证计划 (Verification Plan)
|
||||
|
||||
### 自动化单元测试
|
||||
* 针对 Go 中的 PoW 核心算法,编写单测 `openflare-server/service/cap_test.go`。
|
||||
* 运行单测命令:`go test -v ./openflare-server/service/...`
|
||||
|
||||
### 手动功能与防暴力破解验证
|
||||
1. 打开控制台选项开启 `CapLoginEnabled`。
|
||||
2. 访问登录页面,观察人机验证组件静默加载并完成 PoW 计算,输入正确账户成功登录。
|
||||
3. 使用 `curl` 模拟恶意爬虫不携带或携带错误的 `cap_token` 对登录 API 发起 POST 请求,预期被拦截并返回“验证码错误”。
|
||||
4. 使用已被核销的同一 `cap_token` 二次请求登录,验证防重放失效机制。
|
||||
@@ -0,0 +1,147 @@
|
||||
# OpenFlare 引用替换为 GitHub 路径方案评估计划
|
||||
|
||||
## 1. 目标与背景 (Goal & Context)
|
||||
* **需求背景**:当前 OpenFlare 内部组件(Server、Agent、Relay、Flared)之间采用本地包名引用(例如 `openflare`、`openflare-agent`),并使用 Go `replace` 相对路径指向本地目录。这导致代码无法直接以标准的 GitHub 路径(如 `github.com/rain-kl/openflare`)进行分发、远程安装或被外部引用(例如 `go install` 远程二进制会因为 replace 指令失效而报错)。
|
||||
* **评估目标**:评估将本地引用替换为 `github.com/rain-kl/openflare` 格式的两种可行方案(单模块 Monorepo 方案 vs 多模块 Multi-Module 方案),分析各自的优缺点、工作量及对现有 CI/CD、Docker 镜像构建的影响,给出推荐方案。
|
||||
|
||||
## 2. 设计与决策 (Design & Decisions)
|
||||
|
||||
### 方案 A:标准 Go 多模块方案 (Multi-Module with Sub-paths)
|
||||
保留当前 4 个独立的 Go 模块结构,在各自的 `go.mod` 中将模块名改写为符合 GitHub 结构的子路径:
|
||||
- `openflare-server/go.mod` -> `module github.com/rain-kl/openflare/openflare-server`
|
||||
- `openflare-relay/go.mod` -> `module github.com/rain-kl/openflare/openflare-relay`
|
||||
- `openflare-agent/go.mod` -> `module github.com/rain-kl/openflare/openflare-agent`
|
||||
- `openflared/go.mod` -> `module github.com/rain-kl/openflare/openflared`
|
||||
|
||||
同时,其他模块(Relay, Agent, Flared)的 `go.mod` 中的 `replace` 修改为:
|
||||
`replace github.com/rain-kl/openflare/openflare-server => ../openflare-server`
|
||||
|
||||
#### 优缺点分析:
|
||||
* **优点**:
|
||||
- **模块边界清晰**:各二进制模块依赖独立。例如 `openflare-agent` 不会引入 Server 依赖的 GORM、Gin、Swagger 等库,保持各自模块的 `go.sum` 纯净。
|
||||
- **改动小**:对 Dockerfile 和 GitHub Workflows 影响极小,构建上下文仍可保持原样。
|
||||
* **缺点**:
|
||||
- **远程安装不可用**:仍然需要在 `go.mod` 中保留 `replace` 指令。由于 Go 不允许在远程 `go install` 或 `go get` 时解析本地相对路径的 `replace` 指令,用户依然无法直接通过 `go install github.com/rain-kl/openflare/openflared/cmd/flared@latest` 安装,必须先克隆整个仓库到本地再构建。
|
||||
|
||||
---
|
||||
|
||||
### 方案 B:统一单模块方案 (Unified Single Module Monorepo - 推荐)
|
||||
将整个仓库合并为一个 Go 模块。在仓库根目录下创建 `go.mod`,模块名为 `github.com/rain-kl/openflare`,并删除子目录中的所有 `go.mod` 和 `go.sum`。
|
||||
|
||||
所有内部包导入路径统一改写为:
|
||||
- `"github.com/rain-kl/openflare/openflare-server/..."`
|
||||
- `"github.com/rain-kl/openflare/openflare-relay/..."`
|
||||
- `"github.com/rain-kl/openflare/openflare-agent/..."`
|
||||
- `"github.com/rain-kl/openflare/openflared/..."`
|
||||
|
||||
#### 优缺点分析:
|
||||
* **优点**:
|
||||
- **彻底摆脱 replace**:完全不需要在 `go.mod` 中写 `replace` 指令,代码清爽、易于维持。
|
||||
- **支持远程 Go 工具链**:用户和开发者可以直接使用 `go install github.com/rain-kl/openflare/openflared/cmd/flared@latest` 或 `go install github.com/rain-kl/openflare/openflare-agent/cmd/agent@latest` 远程下载并安装最新二进制。
|
||||
- **版本依赖统一**:所有组件共享相同的依赖版本,避免了组件间因第三方库版本不一致导致潜在的运行时兼容问题。
|
||||
* **缺点**:
|
||||
- **依赖库大一统**:根目录的 `go.mod` 会包含 Server、Agent、Relay 等所有组件的依赖,但这只影响开发时的依赖下载,对最终编译出的二进制大小和运行效率**没有任何影响**(Go 编译器会自动进行死代码消除/树摇)。
|
||||
- **构建配置变动**:Dockerfile 以及 GitHub Actions 需要修改构建上下文,从原本 COPY 子目录改为从根目录统一进行 COPY 和 `go build`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 具体修改文件清单 (Proposed Changes)
|
||||
如果采用**方案 B(推荐)**,需要修改的文件清单和逻辑如下:
|
||||
|
||||
### 根目录与配置文件
|
||||
* #### [NEW] [go.mod](file:///Users/ryan/DEV/Go/OpenFlare/go.mod)
|
||||
- 职责:全局单一 Go 模块定义,模块名:`github.com/rain-kl/openflare`。
|
||||
* #### [DELETE] `openflare-server/go.mod` / `go.sum`
|
||||
* #### [DELETE] `openflare-relay/go.mod` / `go.sum`
|
||||
* #### [DELETE] `openflare-agent/go.mod` / `go.sum`
|
||||
* #### [DELETE] `openflared/go.mod` / `go.sum`
|
||||
|
||||
### 源代码文件 (约 252 个 Go 文件)
|
||||
* #### [MODIFY] `openflare-server/**/*.go`
|
||||
- 职责:将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
|
||||
* #### [MODIFY] `openflare-relay/**/*.go`
|
||||
- 职责:将 `import "openflare-relay/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-relay/..."`,将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
|
||||
* #### [MODIFY] `openflare-agent/**/*.go`
|
||||
- 职责:将 `import "openflare-agent/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-agent/..."`,将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
|
||||
* #### [MODIFY] `openflared/**/*.go`
|
||||
- 职责:将 `import "openflare-flared/..."` 替换为 `import "github.com/rain-kl/openflare/openflared/..."`,将 `import "openflare/..."` 替换为 `import "github.com/rain-kl/openflare/openflare-server/..."`。
|
||||
|
||||
### Dockerfile & Workflows
|
||||
|
||||
如果采用**方案 B(推荐)**,我们将继续保持每个组件(Server、Agent、Relay、Flared)编译并产生自己独立的 Docker 镜像(共 4 个镜像),但其 Dockerfile 的构建上下文(Build Context)统一提升至仓库根目录。具体调整细节如下:
|
||||
|
||||
* #### [MODIFY] [openflare-server/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/Dockerfile)
|
||||
- 职责:由于 `openflare-server` 中没有独立的 `go.mod`,构建上下文必须在**仓库根目录**执行。
|
||||
- 修改内容:
|
||||
```dockerfile
|
||||
# 更改 go-builder 阶段的 COPY 方式:
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY openflare-server/ ./openflare-server/
|
||||
# go build 指定编译子包:
|
||||
RUN go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-server/common.Version=$VERSION'" -o openflare ./openflare-server
|
||||
```
|
||||
|
||||
* #### [MODIFY] [openflare-relay/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflare-relay/Dockerfile)
|
||||
- 职责:适配单 go.mod 构建上下文。
|
||||
- 修改内容:
|
||||
```dockerfile
|
||||
# 更改 builder 阶段的 COPY 方式:
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY openflare-server/ ./openflare-server/
|
||||
COPY openflare-relay/ ./openflare-relay/
|
||||
# go build 指定编译子包:
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-relay/internal/config.Version=$VERSION'" -o openflare-relay ./openflare-relay/cmd/relay
|
||||
```
|
||||
|
||||
* #### [MODIFY] [openflare-agent/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflare-agent/Dockerfile)
|
||||
- 职责:适配单 go.mod 构建上下文。
|
||||
- 修改内容:
|
||||
```dockerfile
|
||||
# 更改 builder 阶段的 COPY 方式:
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY openflare-server/ ./openflare-server/
|
||||
COPY openflare-agent/ ./openflare-agent/
|
||||
# go build 指定编译子包:
|
||||
RUN go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflare-agent/internal/config.Version=$VERSION'" -o /build/openflare-agent ./openflare-agent/cmd/agent
|
||||
```
|
||||
|
||||
* #### [MODIFY] [openflared/Dockerfile](file:///Users/ryan/DEV/Go/OpenFlare/openflared/Dockerfile)
|
||||
- 职责:适配单 go.mod 构建上下文。
|
||||
- 修改内容:
|
||||
```dockerfile
|
||||
# 更改 builder 阶段的 COPY 方式:
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY openflare-server/ ./openflare-server/
|
||||
COPY openflared/ ./openflared/
|
||||
# go build 指定编译子包:
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags "-s -w -X 'github.com/rain-kl/openflare/openflared/internal/config.Version=$VERSION'" -o flared ./openflared/cmd/flared
|
||||
```
|
||||
|
||||
* #### [MODIFY] [.github/workflows/release.yml](file:///Users/ryan/DEV/Go/OpenFlare/.github/workflows/release.yml)
|
||||
- 职责:更新 go build 构建命令及 ldflags 版本注入参数(例如将 `-ldflags "-X 'openflare/common.Version=$VERSION'"` 替换为 `-ldflags "-X 'github.com/rain-kl/openflare/openflare-server/common.Version=$VERSION'"`,同时编译命令需要指向正确的子包目录,如 `./openflare-server`)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 验证计划 (Verification Plan)
|
||||
|
||||
### 编译与运行测试
|
||||
* 运行单测以确保各包逻辑正常:
|
||||
`go test ./...`(在根目录执行)
|
||||
* 本地编译各个二进制:
|
||||
`go build -o bin/openflare-server ./openflare-server`
|
||||
`go build -o bin/openflare-agent ./openflare-agent/cmd/agent`
|
||||
`go build -o bin/openflare-relay ./openflare-relay/cmd/relay`
|
||||
`go build -o bin/openflared ./openflared/cmd/flared`
|
||||
* 启动服务并检查版本输出:
|
||||
`./bin/openflare-server --version`
|
||||
|
||||
### Docker 构建验证
|
||||
* 验证镜像构建命令:
|
||||
`docker build -t openflare-server -f openflare-server/Dockerfile .`
|
||||
`docker build -t openflare-agent -f openflare-agent/Dockerfile .`
|
||||
`docker build -t openflare-relay -f openflare-relay/Dockerfile .`
|
||||
`docker build -t openflared -f openflared/Dockerfile .`
|
||||
@@ -0,0 +1,32 @@
|
||||
# AI 接手计划模板
|
||||
|
||||
说明:本模板用于在 AI 代理上下文发生截断、压缩(Compaction)或将任务转移给另一个 AI 代理时使用,帮助新接手的 AI 快速恢复 100% 的工作状态。
|
||||
|
||||
---
|
||||
|
||||
## 1. 当前任务状态 (Current Status)
|
||||
* **主线任务描述**:用一句话说清楚当前正在解决的核心问题。
|
||||
* **开发分支/提交**:记录当前的工作目录、修改的未暂存文件、或 Git 临时分支名。
|
||||
* **已完成内容 (Completed)**:
|
||||
- [x] 功能 A 后端接口及单测
|
||||
- [x] 前端面板表单组件
|
||||
- **进行中内容 (In Progress)**:
|
||||
- [/] 配置文件渲染与重写模块
|
||||
- **待处理内容 (To Do)**:
|
||||
- [ ] 边缘节点同步下载与校验落地
|
||||
- [ ] 发布功能整体连通性验证
|
||||
|
||||
## 2. 核心文件与上下文 (Key Files & Context)
|
||||
列出与当前开发高度相关的核心文件以及需要注意的特殊背景:
|
||||
* `file:///path/to/core_file.go#L100-L150`:此处负责...,修改时需要注意...
|
||||
* `file:///path/to/frontend_component.tsx`:用于展现...
|
||||
|
||||
## 3. 待决策与遗留问题 (Outstanding Decisions & Issues)
|
||||
* [ ] **疑问/阻塞点**:是否需要支持某某场景?目前是如何兜底处理的?
|
||||
* [ ] **异常与缺陷**:单测 `./controller/...` 运行时目前有 1 个 Fail,失败原因为...
|
||||
|
||||
## 4. 下一步行动指南 (Next Steps)
|
||||
新接手 AI 进来后应当立即执行的前 3 步命令或编辑操作:
|
||||
1. **第一步**:执行 `go test ./controller/...` 确认环境并复现 Fail 异常。
|
||||
2. **第二步**:修改 `openflare-server/controller/xxx.go` 中的逻辑以修复该 Fail。
|
||||
3. **第三步**:在管理端前端页面调试 xxx 表单的提交是否正常。
|
||||
@@ -0,0 +1,47 @@
|
||||
# 功能开发实现计划模板
|
||||
|
||||
说明:本模板用于指导新特性或重大模块开发前的技术规划,明确需求、范围与设计决策。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标与背景 (Goal & Context)
|
||||
* **需求背景**:说明为什么要开发这个特性,解决什么业务痛点或安全隐患。
|
||||
* **开发范围 (Scope)**:明确 V1 阶段的核心交付指标。哪些是本次必做的,哪些是留到后续迭代的(Out of Scope)。
|
||||
|
||||
## 2. 设计与决策决策 (Design & Decisions)
|
||||
* **核心对象/数据模型**:
|
||||
* 说明是否需要修改或新增数据库表(Gorm 结构体、Migration SQL,包括新增字段与关联)。
|
||||
* **API 与鉴权设计**:
|
||||
* 详细定义新增的 REST API 路由、请求载荷(Payload JSON)与响应格式。
|
||||
* **数据流与架构图**:
|
||||
* 使用 Mermaid 绘制数据或控制流的流向。
|
||||
* **设计决策权衡**:
|
||||
* 记录为何选用方案 A 而非方案 B。
|
||||
|
||||
## 3. 具体修改文件清单 (Proposed Changes)
|
||||
按模块或组件列出需要修改的物理文件路径及修改点:
|
||||
|
||||
### 后端 Server
|
||||
* #### [NEW] `openflare-server/model/entity.go`
|
||||
* 职责:...
|
||||
* #### [MODIFY] `openflare-server/service/feature.go`
|
||||
* 职责:...
|
||||
|
||||
### 边缘 Agent 与 OpenResty
|
||||
* #### [MODIFY] `openflare-agent/sync/sync.go`
|
||||
* 职责:...
|
||||
|
||||
### 前端 Web
|
||||
* #### [NEW] `openflare-server/web/features/feature-view.tsx`
|
||||
* 职责:...
|
||||
|
||||
---
|
||||
|
||||
## 4. 验证计划 (Verification Plan)
|
||||
|
||||
### 自动化单元测试
|
||||
* 运行的单测命令,如:`go test -v ./service/...`
|
||||
|
||||
### 数据面重载与生效验证
|
||||
* 说明如何验证新配置在数据面落地。
|
||||
* 提供验证测试的 `curl` 指令或手动操作路径。
|
||||
@@ -0,0 +1,16 @@
|
||||
# 开发计划与 AI 接手
|
||||
|
||||
本分区用于存放正在进行的开发计划(Plan)以及 AI 代理之间的工作接手计划(Handover)。这能帮助不同的 AI 代理快速掌握当前项目状态、历史上下文与后续开发步骤。
|
||||
|
||||
## 计划模板
|
||||
|
||||
在创建具体的开发计划或接手文档时,请使用以下标准模板进行初始化:
|
||||
|
||||
1. **[实现计划模板](./implementation-plan-template.md)**:用于新功能开发或重大重构前的技术方案规划。
|
||||
2. **[AI 接手计划模板](./handover-plan-template.md)**:用于在上下文截断、压缩或更换 AI 代理时,记录当前任务状态、已完成内容与下一步执行计划。
|
||||
|
||||
## 使用建议
|
||||
|
||||
* **命名规范**:正在进行的开发计划建议命名为 `docs/plan/YYYYMMDD-[feature-name].md`,接手计划建议命名为 `docs/plan/handover-[task-name].md`。
|
||||
* **物理隔离**:本目录下的计划文件只在开发周期内进行更新。当对应功能开发完毕并上线后,相应的计划文档应予以保留或归档,以供日后维护与新 AI 追溯历史决策。
|
||||
* **禁止空文件**:请确保新创建的计划文档均基于对应的模板进行初始化填充。
|
||||
@@ -0,0 +1,2 @@
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
@@ -1,46 +0,0 @@
|
||||
# API 约定
|
||||
|
||||
OpenFlare 的管理端 API 与 Agent API 都使用 JSON。
|
||||
|
||||
## 响应结构
|
||||
|
||||
成功与失败都应返回清晰的 `message`:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
## 路径约定
|
||||
|
||||
| 类型 | 约定 |
|
||||
| --- | --- |
|
||||
| 管理端 API | 由管理端 Session 鉴权 |
|
||||
| Agent API | 固定放在 `/api/agent/*` |
|
||||
| 只读接口 | 使用 `GET` |
|
||||
| 变更类接口 | 使用 `POST` |
|
||||
|
||||
## 鉴权
|
||||
|
||||
管理端继续复用现有登录、角色与 Session。
|
||||
|
||||
Agent 正式请求统一使用节点专属 `agent_token`,首次接入可使用全局 `discovery_token`。Agent 请求头固定为:
|
||||
|
||||
```http
|
||||
X-Agent-Token: <token>
|
||||
```
|
||||
|
||||
日志中不得打印完整 Token。
|
||||
|
||||
## Swagger
|
||||
|
||||
登录管理端后可访问:
|
||||
|
||||
```text
|
||||
/swagger/index.html
|
||||
```
|
||||
|
||||
Swagger 文件位于 `openflare_server/docs`,由 `swag init` 生成。
|
||||
+69
-9
@@ -1,12 +1,14 @@
|
||||
# 命令与脚本
|
||||
|
||||
你会学到:OpenFlare Server、管理端前端、Agent、Swagger 和文档站的常用启动、构建、测试、安装与卸载命令。
|
||||
|
||||
## Server
|
||||
|
||||
源码启动:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
export SESSION_SECRET='replace-with-random-string'
|
||||
cd openflare-server
|
||||
export JWT_SECRET='replace-with-random-string'
|
||||
export SQLITE_PATH='./openflare.db'
|
||||
export LOG_LEVEL='info'
|
||||
go run .
|
||||
@@ -21,7 +23,7 @@ go run . --port 3000 --log-dir ./logs
|
||||
测试:
|
||||
|
||||
```bash
|
||||
cd openflare_server
|
||||
cd openflare-server
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
@@ -30,7 +32,7 @@ GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
开发:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
cd openflare-server/web
|
||||
pnpm install
|
||||
pnpm dev
|
||||
```
|
||||
@@ -38,33 +40,75 @@ pnpm dev
|
||||
构建静态产物:
|
||||
|
||||
```bash
|
||||
cd openflare_server/web
|
||||
cd openflare-server/web
|
||||
pnpm build
|
||||
```
|
||||
|
||||
检查:
|
||||
|
||||
```bash
|
||||
cd openflare-server/web
|
||||
pnpm lint
|
||||
pnpm typecheck
|
||||
pnpm test
|
||||
```
|
||||
|
||||
## Agent
|
||||
|
||||
源码运行:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
cd openflare-agent
|
||||
go run ./cmd/agent -config /path/to/agent.json
|
||||
```
|
||||
|
||||
编译:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
cd openflare-agent
|
||||
go build -o openflare-agent ./cmd/agent
|
||||
```
|
||||
|
||||
测试:
|
||||
|
||||
```bash
|
||||
cd openflare_agent
|
||||
cd openflare-agent
|
||||
GOCACHE=/tmp/openflare-go-cache go test ./...
|
||||
```
|
||||
|
||||
## Relay (中继端)
|
||||
|
||||
源码运行:
|
||||
|
||||
```bash
|
||||
cd openflare-relay
|
||||
go run ./cmd -config /path/to/relay.json
|
||||
```
|
||||
|
||||
编译:
|
||||
|
||||
```bash
|
||||
cd openflare-relay
|
||||
go build -o openflare-relay ./cmd
|
||||
```
|
||||
|
||||
## OpenFlared (Client 客户端)
|
||||
|
||||
源码运行:
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go run ./cmd -config /path/to/flared.json
|
||||
```
|
||||
|
||||
编译:
|
||||
|
||||
```bash
|
||||
cd openflared
|
||||
go build -o openflared ./cmd
|
||||
```
|
||||
|
||||
|
||||
## 安装 Agent
|
||||
|
||||
```bash
|
||||
@@ -85,6 +129,22 @@ curl -fsSL https://raw.githubusercontent.com/Rain-kl/OpenFlare/main/scripts/unin
|
||||
|
||||
```bash
|
||||
go install github.com/swaggo/swag/cmd/swag@v1.16.4
|
||||
cd openflare_server
|
||||
cd openflare-server
|
||||
swag init -g main.go -o docs
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
本地预览:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
构建:
|
||||
|
||||
```bash
|
||||
cd docs
|
||||
pnpm build
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user