# 醒了就干：给 Docker 里的 Misskey 重打镜像，升级Node.js 安全更新


最近 TiDB、PHP 都曝出了大漏洞，这两天刚部署 Misskey，也碰上了 😅

我的 Misskey 应用版本为 2026.10.0，容器内 Node.js 为 26.4.0，重新拉取官方镜像后版本未变。

在原镜像上替换 Node.js，重新打包并部署后，运行时升级至 26.12.1，Misskey 应用版本保持不变。

<!--more-->

## 安全更新

[Node.js 官方公告](https://nodejs.org/en/blog/vulnerability/october-2026-security-releases)列出了 10 月 21 日的安全更新，最高严重级别为 High。其中包括 DNS 名称含 NUL 字符时被截断、TLS 主机名校验绕过，以及可能导致远程代码执行的 Brotli 解码器越界写入等问题。公告对应的修复版本是：

| 发布分支 | 本次修复版本 |
| --- | --- |
| 22.x | 22.23.4 |
| 24.x | 24.22.1 |
| 26.x | 26.12.1 |

容器使用 26.x，本次升级至同一大版本的 26.12.1。后续升级应重新核对安全公告。

## 检查容器和上游镜像

Docker 部署的 Misskey 使用镜像内的 Node.js。升级宿主机上的 Node.js，或者单纯重启容器，都不会替换镜像中的运行时。

下面的命令在部署主机执行。示例沿用 `/opt/misskey`、Compose 服务名 `web`，以及容器名 `misskey-web-1`；其他部署需要替换为自己的名称。

```bash
cd /opt/misskey

docker compose ps
docker compose exec -T web node --version

docker inspect misskey-web-1 \
  --format 'IMAGE={{.Config.Image}} IMAGE_ID={{.Image}}'
```

检查结果：

```text
Misskey: 2026.10.0
镜像: misskey/misskey:2026.10.0
Node.js: v26.4.0
```

随后检查上游镜像是否已经更新：

```bash
docker pull misskey/misskey:2026.10.0

docker run --rm --network none --entrypoint node \
  misskey/misskey:2026.10.0 --version

docker buildx imagetools inspect misskey/misskey:latest
```

截至 2026 年 10 月 23 日，官方 `2026.10.0` 镜像内的 Node.js 仍为 26.4.0，`latest` 指向同一个镜像索引。上游尚未更新运行时，重新拉取镜像无法完成升级。

## 保留原镜像，构建修补版本

这次环境是 Linux x86_64，原镜像基于 Debian，使用 glibc。下面下载的 `linux-x64` 官方二进制适用于这一环境；ARM 或 Alpine/musl 镜像不能直接照搬。

### 留下配置和镜像回退点

```bash
set -euo pipefail
cd /opt/misskey

STAMP=$(date -u +%Y%m%dT%H%M%SZ)
BACKUP="/opt/misskey/node-security-backup-$STAMP"
mkdir -m 700 "$BACKUP"
cp -a compose.yaml "$BACKUP/compose.yaml"

BASE_IMAGE=$(docker inspect misskey-web-1 --format '{{.Image}}')
docker tag "$BASE_IMAGE" "local/misskey:before-node-security-$STAMP"
docker tag "$BASE_IMAGE" local/misskey:base-node26.4.0
```

按正在运行的容器镜像 ID 保存本地标签，固定构建基底和回退镜像。此处仅备份 Compose 配置并保留镜像；升级 Misskey 应用或变更数据库前，还需备份数据库和上传文件。

### 下载并校验官方 Node.js

```bash
mkdir -p node-security/node-dist
cd node-security/node-dist

curl -fSLO --retry 3 \
  https://nodejs.org/dist/v26.12.1/node-v26.12.1-linux-x64.tar.xz
curl -fSLO --retry 3 \
  https://nodejs.org/dist/v26.12.1/SHASUMS256.txt

grep ' node-v26.12.1-linux-x64.tar.xz$' SHASUMS256.txt \
  | sha256sum -c -

tar -xJf node-v26.12.1-linux-x64.tar.xz --strip-components=1 \
  node-v26.12.1-linux-x64/bin/node

./bin/node --version
```

预期看到校验 `OK` 和 `v26.12.1`。这里使用 HTTPS 下载并核对官方 SHA-256 清单，没有额外进行发布签名验证。

### 只替换运行时

在 `/opt/misskey/node-security/Dockerfile` 写入：

```dockerfile
FROM local/misskey:base-node26.4.0
COPY node-dist/bin/node /usr/local/bin/node
```

然后构建并单独检查新镜像：

```bash
cd /opt/misskey

docker build --pull=false \
  -t local/misskey:2026.10.0-node26.12.1 ./node-security

docker run --rm --network none --entrypoint node \
  local/misskey:2026.10.0-node26.12.1 \
  -p 'JSON.stringify({node:process.version,modules:process.versions.modules})'
```

输出为：

```json
{"node":"v26.12.1","modules":"147"}
```

新旧运行时的模块 ABI 均为 147，依赖兼容性仍需启动服务验证。

本次仅替换 Node.js 可执行文件，未重新编译 Misskey，也未升级 npm、pnpm 或开发头文件。后续编译原生模块或升级 Misskey 时，需重新检查构建链和基础镜像。

## 修改 Compose，替换 web 容器

原来的 web 镜像配置是：

```yaml
services:
  web:
    image: misskey/misskey:2026.10.0
```

将 web 中这一项替换为下面三项，**其余端口、挂载、网络和环境变量保留**：

```yaml
services:
  web:
    image: local/misskey:2026.10.0-node26.12.1
    pull_policy: never
    build: ./node-security
```

`pull_policy: never` 让部署使用本地修补镜像；`build` 保留重建入口。随后检查配置并替换 web 服务：

```bash
cd /opt/misskey

docker compose config --quiet
docker compose up -d --no-deps --pull never \
  --wait --wait-timeout 180 web
```

这次只重新创建了 web 容器，PostgreSQL 和 Redis 没有重启。web 切换期间会有短暂中断。

原镜像的启动命令包含数据库迁移和应用启动。虽然此次没有改变 Misskey 应用版本，仍要观察启动是否完成，不能看到容器进入 `running` 就认定升级成功。

## 验证正在运行的服务

### 容器和镜像是否一致

```bash
docker inspect misskey-web-1 \
  --format 'IMAGE={{.Config.Image}} IMAGE_ID={{.Image}} STATUS={{.State.Status}} HEALTH={{.State.Health.Status}}'

docker image inspect local/misskey:2026.10.0-node26.12.1 \
  --format '{{.Id}}'
```

容器镜像 ID 与本地修补镜像一致，状态为 `running`，健康检查为 `healthy`。

### 运行时和应用版本是否符合预期

```bash
docker compose exec -T web node -p \
  'JSON.stringify({node:process.version,openssl:process.versions.openssl,modules:process.versions.modules})'

docker compose exec -T web node -p \
  'require("/misskey/package.json").version'
```

实测输出：

```text
{"node":"v26.12.1","openssl":"3.5.9","modules":"147"}
2026.10.0
```

### 页面、API 和日志是否正常

下面域名是占位示例，执行前换成自己的实例地址：

```bash
curl --fail --silent --output /dev/null \
  --write-out 'HTTP %{http_code}\n' https://social.example.com/

curl --fail --silent --request POST \
  --header 'Content-Type: application/json' --data '{}' \
  https://social.example.com/api/ping

docker compose logs --since 5m --tail 100 web
```

这次首页返回 HTTP 200，`/api/ping` 返回包含 `pong` 的 JSON。启动日志显示 worker 已启动并监听端口，数据库和 Redis 也保持健康。

本次验证了应用启动及基本 HTTP/API，未进行漏洞利用复现，也未完整测试登录、上传和联邦交互。

## 回退与后续维护

若新镜像启动失败，且应用版本未变、未新增数据库迁移，可恢复备份的 `compose.yaml`，将 `web.image` 改为已保存的 `local/misskey:before-node-security-时间戳`，并设置 `pull_policy: never`。执行以下命令重新创建容器，再核对镜像 ID：

```bash
cd /opt/misskey
docker compose config --quiet
docker compose up -d --no-deps --no-build --pull never \
  --wait --wait-timeout 180 web
```

官方标签可能已被前面的 `docker pull` 更新，回退必须使用保存的本地标签。回退会恢复旧运行时，仅用于临时恢复服务。

如果以后升级了 Misskey 应用，且已经执行数据库迁移，就不能默认“换回旧镜像”一定能回滚，需要结合该版本迁移说明和数据库备份处理。不要执行 `docker compose down -v` 来升级，它会删除 Compose 管理的数据卷。

当前部署使用的是本地修补镜像。以后官方发布新镜像时，先拉取并独立检查它的 Node.js 版本，再决定是否切回官方镜像；切回时要同步调整 `image`、`pull_policy` 和 `build`，避免继续使用旧修补镜像或误将它重建回来。


---

> 作者: [yuehei](/authors/yuehei/)  
> URL: https://33.ax/posts/misskey-nodejs-security-october-2026/  

