mirror of
https://github.com/okxlin/appstore.git
synced 2026-09-30 00:01:11 +00:00
Add LeafWiki app
This commit is contained in:
@@ -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=
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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
|
||||
Executable
+113
@@ -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
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker-compose down --volumes
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
"$(dirname "$0")/init.sh"
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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 |
Reference in New Issue
Block a user