Add LeafWiki app

This commit is contained in:
okxlin
2026-07-28 08:19:22 +08:00
parent 218c0c1f1d
commit 1e347e3a97
11 changed files with 392 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
PANEL_APP_PORT_HTTP=8080
LEAFWIKI_ADMIN_USERNAME=admin
LEAFWIKI_ADMIN_EMAIL=admin@localhost
LEAFWIKI_ADMIN_PASSWORD=LeafWiki_Admin
LEAFWIKI_ALLOW_INSECURE=false
APP_DATA_DIR=./data
CONTAINER_NAME=
+107
View File
@@ -0,0 +1,107 @@
additionalProperties:
formFields:
- default: 8080
edit: true
envKey: PANEL_APP_PORT_HTTP
labelEn: Port
labelZh: 端口
label:
en: Port
zh: 端口
zh-Hant: 埠
ja: ポート
ko: 포트
ru: Порт
ms: Port
pt-br: Porta
required: true
rule: paramPort
type: number
- default: admin
edit: true
envKey: LEAFWIKI_ADMIN_USERNAME
labelEn: Initial Admin Username
labelZh: 初始管理员用户名
label:
en: Initial Admin Username
zh: 初始管理员用户名
zh-Hant: 初始管理員使用者名稱
ja: 初期管理者ユーザー名
ko: 초기 관리자 사용자 이름
ru: Имя начального администратора
ms: Nama Pengguna Pentadbir Awal
pt-br: Nome do Administrador Inicial
required: true
rule: paramCommon
type: text
- default: admin@localhost
edit: true
envKey: LEAFWIKI_ADMIN_EMAIL
labelEn: Initial Admin Email
labelZh: 初始管理员邮箱
label:
en: Initial Admin Email
zh: 初始管理员邮箱
zh-Hant: 初始管理員電子郵件
ja: 初期管理者メール
ko: 초기 관리자 이메일
ru: Email начального администратора
ms: E-mel Pentadbir Awal
pt-br: E-mail do Administrador Inicial
required: true
type: text
- default: LeafWiki_Admin
edit: true
envKey: LEAFWIKI_ADMIN_PASSWORD
labelEn: Initial Admin Password
labelZh: 初始管理员密码
label:
en: Initial Admin Password
zh: 初始管理员密码
zh-Hant: 初始管理員密碼
ja: 初期管理者パスワード
ko: 초기 관리자 비밀번호
ru: Пароль начального администратора
ms: Kata Laluan Pentadbir Awal
pt-br: Senha do Administrador Inicial
random: true
required: true
rule: paramComplexity
type: password
- default: "false"
edit: true
envKey: LEAFWIKI_ALLOW_INSECURE
labelEn: Allow Plain HTTP Login (Insecure, Trusted LAN Only)
labelZh: 允许明文 HTTP 登录(不安全,仅限可信局域网)
label:
en: Allow Plain HTTP Login (Insecure, Trusted LAN Only)
zh: 允许明文 HTTP 登录(不安全,仅限可信局域网)
zh-Hant: 允許明文 HTTP 登入(不安全,僅限可信區域網路)
ja: 平文 HTTP ログインを許可(非推奨)
ko: 일반 HTTP 로그인 허용(안전하지 않음)
ru: Разрешить вход по HTTP без шифрования (небезопасно)
ms: Benarkan Log Masuk HTTP Biasa (Tidak Selamat)
pt-br: Permitir Login HTTP sem Criptografia (Inseguro)
required: true
type: select
values:
- label: "true"
value: "true"
- label: "false"
value: "false"
- default: ./data
edit: true
envKey: APP_DATA_DIR
labelEn: Data Directory
labelZh: 数据目录
label:
en: Data Directory
zh: 数据目录
zh-Hant: 資料目錄
ja: データディレクトリ
ko: 데이터 디렉터리
ru: Каталог данных
ms: Direktori data
pt-br: Diretório de Dados
required: true
type: text
+1
View File
@@ -0,0 +1 @@
+43
View File
@@ -0,0 +1,43 @@
services:
leafwiki:
image: "ghcr.io/perber/leafwiki:v0.12.0@sha256:a4629aba418ddf6e70f8c9e1f723aff32b3715217cc1b8c3959a43ce2cf8fc2b"
container_name: ${CONTAINER_NAME}
restart: unless-stopped
init: true
user: "1000:1000"
networks:
- 1panel-network
ports:
- "${PANEL_APP_PORT_HTTP}:8080"
env_file:
- path: "${APP_DATA_DIR}/.leafwiki-secrets.env"
required: false
environment:
- LEAFWIKI_DATA_DIR=/app/data
- LEAFWIKI_ADMIN_PASSWORD=${LEAFWIKI_ADMIN_PASSWORD}
- LEAFWIKI_ADMIN_USERNAME=${LEAFWIKI_ADMIN_USERNAME}
- LEAFWIKI_ADMIN_EMAIL=${LEAFWIKI_ADMIN_EMAIL}
- LEAFWIKI_ALLOW_INSECURE=${LEAFWIKI_ALLOW_INSECURE}
- LEAFWIKI_DISABLE_AUTH=false
- LEAFWIKI_PUBLIC_ACCESS=false
read_only: true
tmpfs:
- /tmp:size=32m,mode=1777
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8080/api/health"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3
volumes:
- "${APP_DATA_DIR}:/app/data"
labels:
createdBy: "Apps"
networks:
1panel-network:
external: true
+113
View File
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}"
read_env_value() {
local key="$1"
[[ -f "$ENV_FILE" ]] || return 0
local value
value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)"
case "$value" in
\"*\") value="${value#\"}"; value="${value%\"}" ;;
\'*\') value="${value#\'}"; value="${value%\'}" ;;
esac
printf '%s\n' "$value"
}
configured_value() {
local key="$1"
local default_value="$2"
local value="${!key:-}"
if [[ -z "$value" ]]; then
value="$(read_env_value "$key")"
fi
printf '%s\n' "${value:-$default_value}"
}
prepare_data_dir() {
local raw path secrets_file temp_file jwt_secret totp_key
raw="$(configured_value APP_DATA_DIR ./data)"
[[ -n "$raw" ]] || {
printf '%s\n' 'APP_DATA_DIR must not be empty' >&2
exit 1
}
if [[ "$raw" = /* ]]; then
printf '%s\n' 'APP_DATA_DIR must be relative to the application version directory' >&2
exit 1
fi
path="$(realpath -m -- "$ROOT_DIR/${raw#./}")"
case "$path" in
"$ROOT_DIR"/*) ;;
*)
printf '%s\n' 'APP_DATA_DIR must remain inside the application version directory' >&2
exit 1
;;
esac
install -d -m 0750 "$path"
path="$(realpath -e -- "$path")"
case "$path" in
"$ROOT_DIR"/*) ;;
*)
printf '%s\n' 'APP_DATA_DIR resolves outside the application version directory' >&2
exit 1
;;
esac
chmod 0750 "$path"
chown -R --no-dereference 1000:1000 "$path"
secrets_file="$path/.leafwiki-secrets.env"
if [[ -L "$secrets_file" ]]; then
printf '%s\n' 'LeafWiki secrets file must not be a symbolic link' >&2
exit 1
fi
if [[ ! -e "$secrets_file" ]]; then
umask 077
jwt_secret="$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')"
totp_key="$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')"
[[ ${#jwt_secret} -eq 64 && ${#totp_key} -eq 64 ]] || {
printf '%s\n' 'Failed to generate LeafWiki secrets' >&2
exit 1
}
temp_file="$(mktemp "$path/.leafwiki-secrets.env.tmp.XXXXXX")"
trap 'rm -f -- "${temp_file:-}"' EXIT
printf 'LEAFWIKI_JWT_SECRET=%s\nLEAFWIKI_TOTP_ENCRYPTION_KEY=%s\n' \
"$jwt_secret" "$totp_key" >"$temp_file"
chmod 0600 "$temp_file"
chown 1000:1000 "$temp_file"
mv -f -- "$temp_file" "$secrets_file"
trap - EXIT
fi
[[ -f "$secrets_file" ]] || {
printf '%s\n' 'LeafWiki secrets path must be a regular file' >&2
exit 1
}
[[ "$(grep -c '^LEAFWIKI_JWT_SECRET=' "$secrets_file")" -eq 1 ]] || {
printf '%s\n' 'LeafWiki secrets file must contain exactly one JWT secret' >&2
exit 1
}
[[ "$(grep -c '^LEAFWIKI_TOTP_ENCRYPTION_KEY=' "$secrets_file")" -eq 1 ]] || {
printf '%s\n' 'LeafWiki secrets file must contain exactly one TOTP encryption key' >&2
exit 1
}
jwt_secret="$(sed -n 's/^LEAFWIKI_JWT_SECRET=//p' "$secrets_file")"
totp_key="$(sed -n 's/^LEAFWIKI_TOTP_ENCRYPTION_KEY=//p' "$secrets_file")"
[[ "$jwt_secret" =~ ^[0-9a-f]{64}$ ]] || {
printf '%s\n' 'LeafWiki JWT secret must be a 256-bit hexadecimal value' >&2
exit 1
}
[[ "$totp_key" =~ ^[0-9a-f]{64}$ ]] || {
printf '%s\n' 'LeafWiki TOTP encryption key must be a 256-bit hexadecimal value' >&2
exit 1
}
[[ "$(stat -c '%a' -- "$secrets_file")" = 600 ]] || {
printf '%s\n' 'LeafWiki secrets file permissions must be 0600' >&2
exit 1
}
}
prepare_data_dir
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
docker-compose down --volumes
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
"$(dirname "$0")/init.sh"
+62
View File
@@ -0,0 +1,62 @@
# LeafWiki
## 产品介绍
LeafWiki 是一个轻量级自托管 Wiki,以目录和文件夹结构组织 Markdown 页面,提供全文搜索、标签、页面历史、备份和多用户权限管理。
## 主要功能
- 以目录树组织 Markdown 页面
- 全文搜索、标签和页面链接
- 管理员、编辑者和只读用户角色
- TOTP 双因素认证、快照和 Git 备份
## 访问说明
安装后先在 1Panel 中为应用配置可信 HTTPS 反向代理,再通过 `https://<配置的域名>` 访问,并使用安装时设置的初始管理员用户名和密码登录。初始管理员参数只在首次创建用户数据库时生效;之后请在 LeafWiki 中管理账号和密码。
默认关闭“允许明文 HTTP 登录(不安全)”,保持上游安全默认。应用的明文端口可用于健康检查,但登录必须通过可信 HTTPS 反向代理,并正确传递 `X-Forwarded-Proto: https`。只有在端口严格限制于可信局域网且无法配置 HTTPS 时,才可显式把该选项改为 `true`;此时认证 Cookie 会通过未加密网络传输,绝不能把服务暴露到公网。
## 数据持久化
`APP_DATA_DIR` 挂载到 `/app/data`,保存 Markdown 页面、SQLite 用户数据库、资源、索引、快照和配置。该路径必须位于应用版本目录内,默认值为 `./data`;初始化脚本会拒绝绝对路径和目录外路径,并将其设置给官方非 root 用户 UID/GID `1000:1000`。脚本还会在该目录中首次生成并持久保存两枚 256-bit JWT/TOTP 密钥,不会在升级或重启时重新生成。卸载不会删除绑定目录中的用户数据,升级或迁移前请单独备份。
## 安全与部署风险
- 认证和刷新令牌限流保持启用;包内没有启用公开读取、无认证模式、API 密钥管理或 Git 备份。容器以 UID/GID `1000:1000` 运行,根文件系统只读,丢弃全部 Linux capabilities,并启用 `no-new-privileges`。
- 对固定镜像执行的 2026-07-28 Trivy 扫描发现 `0` 个 Critical 和 `1` 个 High:`CVE-2026-39822`(Go `os.Root` 符号链接目录逃逸)。镜像使用 Go 1.26.4,修复版本为 1.26.5;源码级 Go 漏洞扫描未找到 LeafWiki 调用受影响 `os.Root` API 的路径,但仍应在上游发布修复镜像后尽快更新。
- 同一次源码级 Go 漏洞扫描发现 `GO-2026-5856`(`CVE-2026-42505`,ECH 客户端握手隐私泄露)存在通用 TLS 调用链。该问题只在 LeafWiki 作为客户端使用 Encrypted Client Hello 时生效;默认关闭的 Git 备份和普通入站 HTTP 服务不启用 ECH。扫描还在依赖元数据或二进制符号中报告 `GO-2026-5970`、`GO-2026-5942` 和 `GO-2026-5932`,但未发现 LeafWiki 调用其受影响符号。
## Introduction
LeafWiki is a lightweight self-hosted wiki that organizes Markdown pages in a folder tree and provides full-text search, tags, page history, backups, and multi-user access control.
## Features
- Folder-oriented Markdown page tree
- Full-text search, tags, and page links
- Administrator, editor, and viewer roles
- TOTP two-factor authentication, snapshots, and Git backups
## Usage Notes
Configure a trusted HTTPS reverse proxy for the app in 1Panel, then access it at `https://<configured-domain>` and sign in with the initial administrator credentials selected during installation. Initial administrator settings apply only when the user database is first created; manage later account changes inside LeafWiki.
The package keeps **Allow Plain HTTP Login (Insecure)** disabled by default, matching the upstream secure default. The plain application port remains available for health checks, but login requires a trusted HTTPS reverse proxy that forwards `X-Forwarded-Proto: https`. Enable the insecure option explicitly only when the port is strictly limited to a trusted LAN and HTTPS cannot be configured; authentication cookies then cross the network unencrypted, so never expose that mode to the public Internet.
`APP_DATA_DIR` is mounted at `/app/data` and stores Markdown pages, the SQLite user database, assets, indexes, snapshots, and configuration. It must remain relative to the application version directory and is prepared for UID/GID `1000:1000`. On first install, the initialization script also generates and persists separate 256-bit JWT and TOTP keys in this directory; upgrades and restarts do not regenerate them. Uninstall does not delete bind-mounted user data; back it up before upgrades or migration.
## Security and Deployment Risks
- Authentication and refresh-token rate limiting remain enabled. Public access, authentication bypass, API-key management, and Git backup are not enabled by this package. The container runs as UID/GID `1000:1000`, uses a read-only root filesystem, drops all Linux capabilities, and enables `no-new-privileges`.
- A 2026-07-28 Trivy scan of the pinned image found 0 Critical and 1 High finding: `CVE-2026-39822`, a symlink root escape in Go `os.Root`. The image was built with Go 1.26.4 and the fix is in 1.26.5. Source-mode Go vulnerability analysis found no LeafWiki call path to the affected `os.Root` APIs, but update promptly when upstream publishes a fixed image.
- The same source-mode Go scan found a generic TLS call path for `GO-2026-5856` (`CVE-2026-42505`), an ECH client-handshake privacy leak. It applies only when LeafWiki acts as a client using Encrypted Client Hello; the default-disabled Git backup and ordinary inbound HTTP service do not enable ECH. The scan also reported `GO-2026-5970`, `GO-2026-5942`, and `GO-2026-5932` in dependency metadata or binary symbols without finding calls from LeafWiki to their affected symbols.
## References
- Project: <https://github.com/perber/leafwiki>
- Release: <https://github.com/perber/leafwiki/releases/tag/v0.12.0>
- Container source: <https://github.com/perber/leafwiki/blob/v0.12.0/Dockerfile>
- Configuration: <https://github.com/perber/leafwiki/blob/v0.12.0/.env.example>
- License: <https://github.com/perber/leafwiki/blob/v0.12.0/LICENSE> (MIT)
- Logo source: <https://github.com/lucide-icons/lucide/blob/0.468.0/icons/leaf.svg> (ISC)
+30
View File
@@ -0,0 +1,30 @@
name: LeafWiki
tags:
- Tool
title: 基于目录组织的轻量级 Wiki
description: 基于目录组织的轻量级 Wiki
additionalProperties:
key: leafwiki
name: LeafWiki
tags:
- Tool
shortDescZh: 基于目录组织的轻量级 Wiki
shortDescEn: A lightweight, folder-oriented wiki
description:
en: A lightweight, folder-oriented wiki
zh: 基于目录组织的轻量级 Wiki
zh-Hant: 以目錄組織的輕量級 Wiki
ja: フォルダー指向の軽量 Wiki
ko: 폴더 중심의 경량 Wiki
ru: Легкая Wiki с организацией по папкам
ms: Wiki ringan berasaskan folder
pt-br: Wiki leve organizada por pastas
type: website
crossVersionUpdate: true
limit: 0
website: https://leafwiki.com/
github: https://github.com/perber/leafwiki
document: https://github.com/perber/leafwiki/blob/main/README.md
architectures:
- amd64
- arm64
+23
View File
@@ -0,0 +1,23 @@
LeafWiki app-store logo asset
Source: https://github.com/lucide-icons/lucide/blob/0.468.0/icons/leaf.svg
Source version: Lucide 0.468.0
Modification: rendered as a 180x180 green PNG with a transparent background.
ISC License
Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part
of Feather (MIT). All other copyright (c) for Lucide are held by Lucide
Contributors 2022.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB