Memos 26.11 自部署加固:默认谁先访问谁是管理员,注册还对所有人开放

举报
赛博大妈 发表于 2026/11/09 15:33:04 2026/11/09
【摘要】 把开源笔记 Memos 升到 26.11.0 实测默认配置:监听所有网卡、第一个匿名请求直接成为 ADMIN、注册默认开放、登录可见笔记对陌生注册者可见、登录限流按账号计数会锁住管理员;最后用声明式配置文件在启动时定死管理员和注册开关,并逐项验证。

想在服务器上放一个自己的笔记,Memos 是我这次的选择:Go 写的单文件,SQLite 存数据,几十 MB 内存就能跑。最近它刚发了 26.11.0,前一个版本 26.10.0 又改了一堆东西,所以这次没有直接照着老教程装,而是先在测试机上把默认行为摸了一遍,再决定怎么上正式机。

结论先放这:直接 ./memos 跑起来,离「能放公网」还差好几步。下面按我踩到的顺序记。

测试环境

  • 测试机:Debian 13(trixie),arm64,2 核 8G,没有 Docker,直接用官方二进制。
  • Memos:26.11.0,GitHub Release 里的 memos_26.11.0_linux_arm64.tar.gz。
  • 正式环境打算放在华为云 Flexus 云服务器 L 实例上,前面挂 Caddy 反代。这篇只讲 Memos 本身的默认配置和加固,Flexus 侧的内容会单独标出来。

下载完先对校验和,这步别省:

curl -sL -O https://github.com/usememos/memos/releases/download/26.11.0/memos_26.11.0_linux_arm64.tar.gz
curl -sL -O https://github.com/usememos/memos/releases/download/26.11.0/checksums.txt
grep linux_arm64 checksums.txt
sha256sum memos_26.11.0_linux_arm64.tar.gz

两行哈希都是 1575ed41ce1f668a2e70b50f73c95d2f1b04605db83d4529b8914358b394145e,对得上。压缩包 19MB,解出来的二进制 60MB,./memos --version 输出 26.11.0。

默认启动:它监听在哪

mkdir -p data
./memos --data ./data

启动日志里有一行 Access your memos at: http://localhost:8081,我第一反应是它只听本机。结果看 /proc/net/tcp6,8081 挂在全零地址上,也就是 [::]:8081,所有网卡都在听。日志写的 localhost 只是给你一个能点的地址,不代表绑定地址。

如果机器有公网 IP、安全组又顺手放开了 8081,这个实例从启动那一秒起就对外了。进程常驻内存我看了一下,RSS 大约 45MB,确实轻。

另外一个顺带发现:data/ 下的 memos_prod.db 权限是 -rw-r--r--,同机其他用户都能读。后面会处理。

坑一:谁先访问,谁就是管理员

新实例的 /api/instance/profile 会返回 "needsSetup":true。意思是还没有管理员,等着有人来初始化。网页上就是打开首页让你注册第一个账号。

问题是这个「有人」不挑人。我用一个完全不带认证的请求去建用户,回来的就是 ADMIN:

curl -s -X POST http://127.0.0.1:8081/api/users \
  -H 'Content-Type: application/json' \
  -d '{"username":"your-user","password":"..."}'

返回里 "role":"ADMIN",再查一次 profile,needsSetup 变成了 false。

一开始我把请求体写成 {"user":{"username":...}},拿到的是 invalid username "",以为接口改了权限,其实只是 26.10 之后 REST 接口的请求体直接就是 User 对象,不再套一层。

说白了,从启动到你自己打开网页注册的这段时间,谁先摸到 8081 谁就是这台实例的主人。我之前写 Komari 探针时遇到过一模一样的设计,这类自部署项目的「首次访问即初始化」都要当回事。

坑二:注册默认开放,「登录可见」不等于「只有我能看」

管理员建好以后,我用另一个名字 stranger 再调一次同一个接口,没有任何 token,照样注册成功,角色是 USER。查 GENERAL 设置,disallowUserRegistration 默认就是 false。

这本身还不算大事,麻烦在可见性。Memos 的笔记有三档:PRIVATE、PROTECTED、PUBLIC。我先确认了一下,新用户默认的 memoVisibility 是 PRIVATE,这点做得对。但很多人会把一些想在多设备、多人之间共享的东西设成 PROTECTED,界面上叫「工作区可见」,听着像个小圈子。

我用管理员发了一条 PROTECTED 的笔记,内容随手写了「内网 NAS 地址和备份计划」,然后用 stranger 的 token 去列笔记:

stranger 直接就看到了。不带 token 的匿名请求会被挡掉(401),所以 PROTECTED 的真实含义是「这台实例上任何一个登录用户都能看」。而在注册开放的情况下,「登录用户」等于「任何能访问到页面的人」。

坑三:登录限流按账号算,管理员也会被锁

26.11 默认开着 --rate-limit。我想看看它能不能挡爆破,就对管理员账号连发了 40 次错误密码:

前 10 次是 400(unmatched username and password),第 11 次开始全是 429,错误详情写得很清楚:limit 10、window_seconds 900、scope 是 signin_account。

挡爆破是挡住了。但我接着用正确密码登录,一样是 429,retry_after_seconds 还剩 305 秒。换成 stranger 账号用错误密码试,返回的是正常的 400,说明计数挂在被攻击的账号上,不是挂在来源 IP 上。

这就是个取舍:只要有人知道你的用户名,就能隔一会儿发 10 个错误密码,让管理员一直登不进去。我的处理是不让登录接口直接暴露给全网,见后面的加固部分。

坑四:老脚本和老教程里的 /api/v1 全挂了

我一开始按老习惯调的是 /api/v1/memos,在 26.11.0 上返回:

{"code":12,"message":"API v1 has been removed. Use /api/... for REST and /memos.api.<Service>/<Method> for Connect. ..."}
 [410]

翻了项目里的 proto/api/CHANGELOG.md,26.10.0 那一节写明 REST 路由从 /api/v1/... 挪到 /api/...,并且说这一版对旧路由返回 410,下一个版本直接删掉。网上大部分 Memos 教程里的接口示例都是 v1 的,照抄会一直 410。另外同一节还写了:改密码以后旧的 access token 立即失效,升级前签发的 token 也不再认,第三方客户端升级后要重新登录。

加固:用声明式配置把管理员和注册开关定死

26.10 开始,Memos 支持在配置目录(默认 /etc/memos/config,可用 --config-dir 改)里放 JSON 文件,启动时自动应用。我用它解决前面的坑一和坑二:管理员在启动时就建好,注册直接关掉。

{
  "instanceSettings": [
    { "name": "instance/settings/GENERAL",
      "generalSetting": { "disallowUserRegistration": true } }
  ],
  "users": [
    { "name": "users/your-user", "username": "your-user", "role": "ADMIN",
      "password": "${file:/etc/memos/secrets/admin-password}" }
  ],
  "lifecycle": [
    { "resource": "users/your-user", "preventDestroy": true, "writeOnlyVersion": 1 }
  ]
}

密码不写进 JSON,用 ${file:...} 引用单独的密码文件,路径必须是绝对路径。测试机上我把配置和密码文件放在测试目录里,正式机按上面的路径放。操作顺序:

  1. 用 umask 077 生成密码文件:head -c 18 /dev/urandom | base64 > /etc/memos/secrets/admin-password。
  2. 先校验。测试机上我跑的是 memos config validate --config-dir ./config,输出 Configuration in ./config is valid: 0 identity providers, 1 instance settings, 1 users, 0 linked identities.
  3. 换一个全新的数据目录启动,并且只听本机:
./memos --data ./data --config-dir ./config \
  --addr 127.0.0.1 --port 8081 --trusted-proxies none

这里我还走了个弯路:想先跑 memos config plan 看看会改什么,结果报 no SQLite database at .../memos_prod.db; pass the instance's --data or --dsn。plan 是拿配置和现有数据库做对比的,全新实例还没有库,直接启动让它在启动时应用就行。

启动日志里能看到 applied configuration ... changes="[create instance/settings/GENERAL create users/your-user]",监听地址变成 127.0.0.1:8081。

--trusted-proxies 默认是 private,也就是信任内网段发来的转发头。测试时没有反代我就设成了 none;正式机前面有 Caddy 在本机转发,按 --help 的说法保留默认或只填 127.0.0.1 即可(未实测,依据 --help 说明)。

验证

逐项再测一遍:

  • /api/instance/profile 返回 "needsSetup":false,启动完就有管理员,没有空窗期。
  • 匿名注册返回 403:{"code":7, "message":"user registration is not allowed"}。
  • 管理员用密码文件里的密码登录,200。
  • 我又试了用管理员 token 通过接口把注册打开,被拒绝,返回 MANAGED_BY_CONFIGURATION,提示 change generalSetting.disallowUserRegistration there,指向 memos.json。也就是说这个开关以后只能改文件,界面上手滑点不开。

数据库权限那条,我单独用 umask 077 起了一个实例,生成的 memos_prod.db 是 -rw-------,目录是 drwx------。所以权限跟着启动进程的 umask 走,正式机用 systemd 跑的话在 unit 里加 UMask=0077 就行(未实测,测试机没有 systemd)。

放到 Flexus 上还要做的

这部分是正式部署的计划,测试机上没法验证,都标了出来:

  • Memos 只听 127.0.0.1,外面靠 Caddy 反代 443。Caddy 那层的配置和踩坑我之前单独写过一篇,这里不重复。
  • 华为云 Flexus L 实例的安全组入方向只放 22 和 443,8081 不放;SSH 再收窄到自己的 IP(未实测,依据安全组规则配置方式)。
  • 坑三的锁号问题,如果只有自己用,可以在 Caddy 上按来源 IP 限制 /api/auth/signin 或整个站点(未实测,依据 Caddy 文档)。
  • 备份就是 data/ 目录,SQLite 有 WAL 文件,备份时连 -wal、-shm 一起带上,或者停服务再拷。

小结

  • 26.11.0 默认监听所有网卡,日志里的 localhost 不是绑定地址,用 --addr 127.0.0.1 改掉。
  • 新实例第一个匿名请求就是 ADMIN,注册默认开放;用声明式配置在启动时建管理员、关注册,空窗期为零。
  • PROTECTED 是「所有登录用户可见」,注册开着时等于对外可见。
  • 登录限流是每账号 15 分钟 10 次,正确密码也会被挡,登录口别直接裸露在公网。
  • 老教程里的 /api/v1 在 26.10 之后全是 410,脚本要改成 /api/...。
  • 启动前设 umask 077,数据库才不是 644。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。