外网访问
雲瞰 は初期状態では LAN 内でのみ利用できます——外出するとスマホアプリから自宅のサーバーに届きません。本章では 雲瞰 を安全に外部公開する 3 つの方法(パブリック直結(推奨)、メッシュ VPN、リバースプロキシ)と、それぞれ必要なポートを説明します。パブリック直結に必要なのはドメイン 1 つとルーターの 1 ルールだけで、証明書は管理画面から自動取得されます。
まずこれだけは
24214 / 23880)は絶対に転送しないでください——これらの前段にログインはなく、転送はカメラをインターネット中のスキャナーに差し出すのと同じです。3 つの方法の選び方
| 方法 | 必要なもの | ルーターでの作業 | ライブ映像の遅延 | 向いている人 |
|---|---|---|---|---|
| パブリック直結 | ドメイン 1 つ(無料サブドメイン可)、証明書は自動取得 | IPv4:TCP 転送 1 本。IPv6:受信許可のみ | WebRTC で 0.5 秒未満 | グローバル IP か IPv6 がある回線(推奨) |
| メッシュ VPN | 使う端末すべてに同じアプリを導入 | 何もしない | WebRTC で 1 秒未満 | CGNAT かつ IPv6 なし、またはポートを一切開けたくない場合 |
| リバースプロキシ + ドメイン | すでに動かしている nginx / Caddy / Lucky | プロキシが使うポートを転送 | 既定は HLS 約 2.5 秒(TCP 透過で WebRTC 化可) | すでにプロキシを運用し 443 に統一したい人 |
3 つは排他ではなく、同時に有効化できます。迷ったらこの順に:まず回線に IPv6 があるか確認——あればパブリック直結はポート転送すら不要です。IPv6 はないがグローバル IP がある場合も同じくパブリック直結。どちらもない(CGNAT)ならメッシュ VPN です。
方法 1:パブリック直結(推奨)
雲瞰 は管理画面・アプリ API・ライブ映像をすべて 1 つの TCP ポートに集約します。同じポートで HTTPS と WebRTC の映像チャネルの両方を受け、サーバー側が自動的に振り分けます。したがってルーターの設定は1 本だけで、WebRTC 用に UDP を別途転送する必要はありません。
この方式が解決すること
手順 1:ドメインと証明書(管理画面から自動取得)
パブリック直結には HTTPS が必須です。スマホアプリは信頼されない自己署名証明書を拒否するため、自分のドメインと正式な証明書が要ります。設定 → 一般 → HTTPS 証明書 でドメインと DNS プロバイダーの API キーを入力して保存すると、雲瞰 が Let's Encrypt へ自動申請し、期限前に自動更新します。
- 80 番ポートを開ける必要はありません。DNS-01 認証(ドメイン配下に一時的な TXT レコードを書く方式)なので、受信 80 が塞がれている回線でも取得できます。
- 対応 DNS プロバイダー:Alibaba Cloud、Tencent Cloud DNSPod、Huawei Cloud、Cloudflare、AWS Route 53、Porkbun、deSEC、DuckDNS。
- 自分のドメインがなくても使えます:deSEC と DuckDNS は無料のサブドメインを配布しており、それを自分のドメインとして利用できます。
- すでに証明書がある場合は
fullchain.pemとprivkey.pemをdata/certs/に置くだけです。雲瞰 はそれを使うだけで、あなたのファイルを上書きしません。 - 申請前にセルフチェックを実行できます。実際にテスト用レコードを書いて削除し、API キーと権限を確認するもので、Let's Encrypt の発行回数を消費しません。
ドメインはご自身のものが必要です
手順 2:公開ポートを選ぶ
家庭用回線では 443 を避けたほうが無難です
23443 が手頃です。クラウドサーバーやその他の地域では制限がないため 443 で構いません(URL にポート番号を書かずに済みます)。同じパネルで公開ポートを入力して保存すれば即座に反映されます。バックエンドが設定を作り直し、そのポートが実際に待ち受け状態になったことまで確認します。コンテナの再起動も compose の編集も不要です。ポートが他のプログラムに使われている場合や内部ポートと衝突する場合は、保存時にその場で拒否されます(保存後に静かに失敗することはありません)。
手順 3:ルーター
- IPv4(グローバル IP あり):ポート転送を 1 本追加し、外側の
23443/tcpを 雲瞰 ホストの23443へ。必要なのはこれだけで、23515などを転送する必要はありません。 - IPv6:NAT がないため転送は不要です。ルーターの IPv6 ファイアウォールで
23443/tcp宛の受信を許可してください。 - DNS:グローバル IPv4 は A レコード、IPv6 は AAAA レコード。家庭回線のアドレスは変わるので、DDNS クライアントで自動更新させます。
手順 4:確認
https://<あなたのドメイン>:23443/ を開き、ログインしてライブ画面へ。1 秒以内に、WebRTC 経由で映像が出るはずです。
同じパネルに外部到達性のセルフチェックがあります。ドメインの解決先、ポートの開通、証明書の妥当性を確認し、どの段階で止まっているかを直接示します——パケットキャプチャは不要です。
グローバル IP がもらえない場合
方法 2:メッシュ VPN(ポートを一切開けない)
组网工具(Tailscale / WireGuard / ZeroTier 等)在你的手机和家里的服务器之间建一条加密隧道,手机像在家一样用内网地址访问 云瞰——不需要在路由器上开任何端口,服务器完全不暴露到公网。
- 1
服务器装组网客户端
在跑 云瞰 的那台机器(或同 LAN 的软路由)装 Tailscale,然后
tailscale up登录。WireGuard / ZeroTier 同理。bashcurl -fsSL https://tailscale.com/install.sh | sh sudo tailscale up - 2
手机装同款 App 并登录同一账号
手机端装对应的 Tailscale / ZeroTier App,登录与服务器相同的账号,两端就进了同一个虚拟局域网。
- 3
App 里填组网地址
云瞰 App 的服务器地址填组网分配的 IP——Tailscale 是
100.x.x.x,ZeroTier 是10.x.x.x,端口仍然是:23406,例如http://100.x.x.x:23406。 - 4
完成
出门后手机连 4G/5G 也能访问,体验和在家一样。无需公网 IP、无需域名、无需 HTTPS 证书。
メッシュ VPN の得失
方法 3:リバースプロキシ + ドメイン
如果你有公网 IP + 一个域名,想用 https://cam.example.com 这种地址访问(方便发给不方便装组网 App 的家人),可以在 云瞰 前面架一层 nginx / Caddy 做 HTTPS 终止。云瞰 容器内部已自带一层 nginx,外层反代只需把流量整体转发给 :23406。
プロキシ経由でライブ映像はどうなるか
stream ブロック、Caddy の layer4 プラグイン)によりポートをそのまま 雲瞰 の公開ポートへ透過させてください。両立可能です:管理画面は 443 のプロキシ経由、ライブ映像は透過ポート経由。nginx 配置
把下面内容整份存为 /etc/nginx/sites-available/skyview.conf,替换 <你的域名> 后 ln -s 到 sites-enabled/ —— 一个文件搞定,不用再单独建 snippet。两个 location / 里的 proxy_set_header 块完全一样:nginx 的 proxy_set_header 是覆盖不继承,每个 location 必须各带一份,照抄即可。
# WebSocket Upgrade 透传 —— 必须在 http {} context
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name <你的域名>;
# certbot --nginx 会自动把此块改成 301 跳 https
location / {
proxy_pass http://127.0.0.1:23406;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme; # ★ 漏了它 HTTPS 部署会登录死循环
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_connect_timeout 60s;
proxy_send_timeout 1d;
proxy_read_timeout 1d;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name <你的域名>;
ssl_certificate /etc/letsencrypt/live/<你的域名>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<你的域名>/privkey.pem;
# 录像导出、人脸库批量导入可能上百 MB;长 JWT cookie 较大防 414
client_max_body_size 200m;
client_header_buffer_size 4k;
large_client_header_buffers 8 16k;
location / {
proxy_pass http://127.0.0.1:23406;
proxy_http_version 1.1;
# ↓ 与上面 :80 location 完全相同,逐行照抄(proxy_set_header 不继承)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_connect_timeout 60s;
proxy_send_timeout 1d;
proxy_read_timeout 1d;
}
}X-Forwarded-Proto / X-Forwarded-Host 必须传 + 每个 location 都要 include
X-Forwarded-Proto 判断客户端真实 scheme、靠 X-Forwarded-Host 知道对外域名。漏 X-Forwarded-Proto → HTTPS 部署下登录后立刻被踢回登录页、直播画面被浏览器当 Mixed Content 拦掉;漏 X-Forwarded-Host → 录像回放 / 下载 / 导出的链接拼成错误地址,客户端打不开。上面的 nginx 模板和 Caddy(默认即传这两个头)都已覆盖,照抄即可——端口无需手动配 X-Forwarded-Port,容器内 nginx 会自动按「外层反代 / 裸 IP 直连」两种场景兜好。另外 nginx 的 proxy_set_header 是覆盖不是追加:某个 location 只要写了任意一条,上层的 set_header 全部失效——所以每个 location 都要 include 完整那一份 snippet。申请证书(机器要能从公网访问 :80):
certbot --nginx -d <你的域名> -m <你的邮箱> --agree-tos --no-eff-email --redirectCaddy 配置(更简单)
Caddy 自动签发 / 续期 Let's Encrypt 证书,无需 certbot,配置短很多。/etc/caddy/Caddyfile:
<你的域名> {
reverse_proxy 127.0.0.1:23406 {
# Caddy 默认就传 X-Forwarded-{For,Proto,Host}
# 长连接(对讲 WS / 事件 SSE / 直播流)调大 flush + 超时
flush_interval -1
transport http {
read_timeout 24h
write_timeout 24h
}
}
request_body {
max_size 200MB
}
}ポート早見表
| ポート | プロトコル | 用途 | 外部に開ける? |
|---|---|---|---|
23443 | TCP | パブリック直結:管理画面 + API + ライブ映像(HTTPS、変更可) | 開ける(この方式で開ける唯一のポート) |
23406 | TCP | LAN 用の入口(平文 HTTP) | 開けない(外部からは上記 23443 を使用) |
23515 | UDP + TCP | ライブ映像のメディアストリーム(LAN / メッシュ / IPv6 直結) | 不要 |
23880 | TCP | RTSP。LAN 内の外部プレイヤー向け | 不要 |
24214 | TCP | 内部ストリーミングサービス | 厳禁 |
内部ポートは絶対に転送しない
24214(内部ストリーミングサービス)と 23880(RTSP)にはログインの層がありません——配信の認可は 23406 / 23443 の入口で行われます。これらを公開すると認証を丸ごと迂回され、カメラ映像が公開状態になります。23406 をそのまま転送するのも同様に避けてください:平文 HTTP のためパスワードが平文で流れ、さらにブラウザや Android 9 以降は安全でないページでカメラ権限を拒否します。外部アクセスには上記のパブリック直結(23443、HTTPS)を使ってください。配完怎么验证
# 1. HTTP→HTTPS 跳转(反代场景)
curl -sSI http://<你的域名>/healthz | head -1 # 期望 301
# 2. 健康检查
curl -sS https://<你的域名>/healthz # 期望 {"code":0,...}
# 3. 未登录访问受保护接口
curl -sS -o /dev/null -w '%{http_code}\n' \
https://<你的域名>/api/cameras # 期望 401最后用浏览器走一遍完整流程:登录 → 实时监控看到画面 → 浏览器控制台没有 Mixed Content 报错。若登录后立刻被踢回登录页、或直播画面报 Mixed Content,99% 是 X-Forwarded-Proto 没传对。更多排查见 排错。