Obsidian Docker 容器浏览器访问完整部署记录

Obsidian Docker 容器浏览器访问完整部署记录

__

Obsidian Docker 容器浏览器访问完整部署记录

环境:飞牛OS (fnOS) + 1Panel + Docker + Nginx Proxy Manager 2.15.1 目标:在局域网内通过浏览器访问 Obsidian 笔记(无公网域名) 最终访问地址https://obsidian.local:30443


一、背景说明

为什么要用 Docker 部署 Obsidian?

Obsidian 没有官方 Web 版,只有桌面客户端(Windows/Mac/Linux)和移动端 App。LinuxServer 的镜像方案是:

完整 Linux 桌面环境(Xfce)
    + Selkies(WebRTC 远程桌面引擎,原 Google 工程师项目)
    + GStreamer 媒体编解码库
    + Obsidian 桌面应用程序

通过 Selkies 把容器内的 Obsidian 桌面以 WebRTC 方式串流到浏览器,变相实现"网页访问"。这也解释了为什么镜像体积高达 3GB+,以及为什么强制要求 HTTPS(WebRTC 安全上下文限制)。

访问链路:

浏览器 → HTTPS/WebSocket → NPM 反向代理 → 容器内 Selkies 引擎 → 容器内 Obsidian 桌面

环境约束

约束

说明

飞牛OS 占用宿主机 80/443 端口

NPM 必须使用非标准端口 30080/30443

纯局域网,无公网域名

无法申请受信任的 CA 证书,只能用自签名证书

Obsidian 前端强制 HTTPS

不能直接 HTTP 访问,必须走反向代理提供 HTTPS


二、最终生效配置

2.1 Obsidian 容器(docker-compose)

networks:
  1panel-network:
    external: true

services:
  obsidian:
    image: lscr.io/linuxserver/obsidian:latest
    container_name: ${CONTAINER_NAME}
    security_opt:
      - seccomp:unconfined        # 官方要求,桌面环境需要更多系统调用
    deploy:
      resources:
        limits:
          cpus: ${CPUS}
          memory: ${MEMORY_LIMIT}
    environment:
      - HTTPS=false               # 容器内部关闭 HTTPS,由 NPM 对外提供
      - PUID=1000
      - PGID=1000
      - TZ=${TIME_ZONE}
    volumes:
      - ./config:/config
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - ${HOST_IP}:${PANEL_APP_PORT_HTTP}:3000
    restart: on-failure:5
    networks:
      - 1panel-network

2.2 NPM Proxy Host 配置

字段

Domain Names

obsidian.local

Forward Scheme

http

Forward Host

1Panel-obsidian(容器名)

Forward Port

3000

WebSocket Support

✅ 开启(必须)

SSL Certificate

自签名 obsidian-local(含 DNS SAN)

2.3 端口映射

宿主机 30443 → NPM 容器 443 → Obsidian 容器 3000

三、遇到的问题与解决过程

问题 1:ERR_SSL_UNRECOGNIZED_NAME_ALERT — TLS 握手直接失败

现象:浏览器和 curl 均报此错误,连 TLS 握手都无法完成。

排查

curl -vk --resolve "192.168.5.224:30443:192.168.5.224" https://192.168.5.224:30443/
# 输出:TLSv1.3 (IN), TLS alert, unrecognized name (624)

根本原因:IP 直连时浏览器不发送 SNI(SNI 是域名专用机制),Nginx 收不到 SNI 就匹配不到任何 server_name,落入 fallback server block,而该 block 配置了:

# /etc/nginx/conf.d/default.conf(NPM 内置)
server {
    listen 443 ssl;
    server_name localhost;
    ssl_reject_handshake on;   # ← 直接拒绝所有不匹配的握手
    return 444;
}

尝试过的无效方案

  • NPM 界面设置 Default Site → 对 SSL 握手阶段无效

  • 手动修改 3.confdefault_server → NPM 每次保存 Proxy Host 会覆盖

最终解决:放弃 IP 直连,改用本地假域名 obsidian.local,彻底绕开 SNI 问题。


问题 2:第一版自签名证书导致 ERR_SSL_UNRECOGNIZED_NAME_ALERT

现象:从 Obsidian 容器内部提取的自带证书,浏览器报错。

原因:该证书未包含 subjectAltName(SAN),现代浏览器要求证书必须有 SAN,仅有 CN 字段不被接受。

解决:在宿主机用 OpenSSL 手动生成包含 IP SAN 的证书(此为过渡方案,后被域名方案替代):

cat > /tmp/openssl-ip.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = 192.168.5.224
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = IP:192.168.5.224
EOF

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout /tmp/obsidian-ip.key \
  -out /tmp/obsidian-ip.crt \
  -config /tmp/openssl-ip.cnf

问题 3:改用域名后证书报 ERR_SSL_KEY_USAGE_INCOMPATIBLE

现象:curl 已能正常握手并返回 HTTP 200,但浏览器仍然报错。

原因:第一版证书的 keyUsage 字段为:

keyUsage = keyEncipherment, dataEncipherment

TLS 1.3 要求证书必须包含 digitalSignature,浏览器比 curl 更严格地执行此检查。

解决:重新生成证书,修正 keyUsage

cat > /tmp/openssl-local.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = obsidian.local
[v3_req]
keyUsage = digitalSignature, keyEncipherment    # ← 关键:加上 digitalSignature
extendedKeyUsage = serverAuth
subjectAltName = DNS:obsidian.local
EOF

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout /tmp/obsidian-local.key \
  -out /tmp/obsidian-local.crt \
  -config /tmp/openssl-local.cnf

验证:

openssl x509 -in /tmp/obsidian-local.crt -text -noout | grep -A3 "Key Usage"
# 期望输出:
# X509v3 Key Usage:
#     Digital Signature, Key Encipherment

四、完整操作步骤(可复现)

Step 1:生成正确的自签名证书

cat > /tmp/openssl-local.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = obsidian.local
[v3_req]
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:obsidian.local
EOF

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout /tmp/obsidian-local.key \
  -out /tmp/obsidian-local.crt \
  -config /tmp/openssl-local.cnf

# 验证 SAN
openssl x509 -in /tmp/obsidian-local.crt -text -noout | grep -A1 "Subject Alternative Name"
# 验证 KeyUsage
openssl x509 -in /tmp/obsidian-local.crt -text -noout | grep -A3 "Key Usage"

Step 2:在 NPM 上传证书

  1. 打开 http://192.168.5.224:30081

  2. SSL CertificatesAdd SSL CertificateCustom

  3. 名称填 obsidian-local,上传 .crt.key,保存

Step 3:配置 NPM Proxy Host

  1. HostsProxy Hosts → 新建或编辑

  2. Details 标签:

    • Domain Names: obsidian.local

    • Forward Scheme: http

    • Forward Hostname: 1Panel-obsidian

    • Forward Port: 3000

    • ✅ Websocket Support

  3. SSL 标签:选择 obsidian-local 证书

  4. 保存

Step 4:添加 hosts 记录(每台访问设备都要做)

Windows(管理员权限编辑 C:\Windows\System32\drivers\etc\hosts):

192.168.5.224   obsidian.local

Mac/Linux

echo "192.168.5.224   obsidian.local" | sudo tee -a /etc/hosts

Step 5:验证

# 服务端验证
curl -vk --resolve "obsidian.local:30443:192.168.5.224" https://obsidian.local:30443/
# 期望:HTTP/1.1 200 OK,subject: CN=obsidian.local

浏览器访问 https://obsidian.local:30443,首次出现证书警告,点击"高级"→"继续前往"即可。


五、关键知识点总结

知识点

说明

SNI(Server Name Indication)

TLS 扩展,客户端在握手时携带目标域名,让服务端选择对应证书。IP 地址不走 SNI,这是 IP 直连 HTTPS 的根本缺陷

ssl_reject_handshake on

Nginx 指令,对没有匹配 server_name 的连接直接在握手阶段返回 alert,是 NPM 的默认 fallback 行为

证书 SAN

现代浏览器要求证书必须有 subjectAltName 字段,仅有 CN 不被接受

TLS 1.3 KeyUsage

必须包含 digitalSignature,否则浏览器报 ERR_SSL_KEY_USAGE_INCOMPATIBLE,curl 不检查此项

Selkies

开源 WebRTC 远程桌面串流框架(原 Google 工程师项目),LinuxServer 用它实现"无 Web 端的桌面应用容器化"


六、已知局限与注意事项

  • hosts 文件需要在每台设备上单独配置,新设备接入局域网时需重复操作(可考虑在路由器/内网 DNS 上统一配置 obsidian.local 解析)

  • NPM 每次在界面保存 Proxy Host 都会重新生成 Nginx 配置文件,手动修改的 listen ... default_server 等改动会丢失

  • 自签名证书浏览器会持续显示警告,可将 obsidian-local.crt 导入系统受信任根证书库消除警告

  • Obsidian 容器内运行完整 Linux 桌面 + Selkies,资源占用较重(镜像 3GB+,运行时内存 400MB+)

docker部署体验不太好,莫名其妙会出现黑屏的情况,可能是内置的linux桌面持续占用空间导致崩掉了,已卸载,配置较好的设备可以尝试。

Nginx Proxy Manager 升级后证书失效故障记录 2026-06-21
Obsidian Self-hosted LiveSync 部署记录 2026-06-22

评论区