1. 为什么非得用 SSH 隧道跑 Jupyter Notebook?——从“本地浏览器打不开远程页面”说起
我第一次在 Ubuntu 20.04 服务器上装好 Jupyter Notebook,兴冲冲地把http://server_ip:8888粘贴进公司电脑的 Chrome,结果页面转三秒直接报错:ERR_CONNECTION_REFUSED。不是密码错,不是端口没开,是压根连不上。后来翻了二十几个帖子才明白:Jupyter 默认绑定的是localhost(也就是127.0.0.1),它根本没打算让外部网络访问——这就像你在家客厅开了个投影仪,但所有窗户都焊死了,外面的人再想看也看不到一帧画面。
这不是 Bug,是设计哲学。Jupyter 的核心定位从来就不是“公开 Web 服务”,而是“本地交互式开发环境”。它默认只监听回环地址,是为了防止未授权访问、命令执行、文件遍历等高危风险。尤其当你在云服务器或实验室集群上运行时,直接暴露8888端口到公网,等于把一把万能钥匙挂在门把手上。热词里反复出现的ssh连接reset by peer、ssh: could not resolve hostname、connection refused,90% 都卡在这一步:人以为配好了,其实 Jupyter 根本没对外“开门”。
SSH 隧道就是那个不拆窗、不换锁,却能让外面人安全看到投影画面的光学中继器。它不改变 Jupyter 的原始配置,也不要求你去改防火墙规则、开云厂商安全组、配 Nginx 反向代理,更不用碰 TLS 证书这种动辄半小时起步的配置。你只需要一条ssh -L命令,就把远端服务器的8888端口,“悄悄”映射到你本地机器的8888端口上。之后你在自己电脑浏览器里访问http://localhost:8888,流量会自动加密穿过 SSH 连接,抵达服务器上的 Jupyter 实例——整个过程对 Jupyter 完全透明,它甚至不知道自己正在被远程访问。
这个方案之所以成为 Ubuntu 20.04 下的工业标准,是因为它同时满足三个硬性条件:零信任网络模型下的最小权限暴露、无需额外服务依赖、与现有 SSH 基础设施无缝集成。你不需要为 Jupyter 单独装一个sshd,不需要学systemd服务管理,不需要研究ufw规则语法。只要你的服务器能ssh登录,这个隧道就能建起来。这也是为什么vscode连接ssh远程服务器、remote ssh、ssh 免输入密码 vscode这些热词会高频出现——它们共享同一套底层通信管道。Jupyter 只是这条管道上跑的一个应用,而不是需要单独打通的关卡。
提示:别被
jupyter notebook 怎么分享这类搜索词带偏。真正的生产级分享从来不是靠“复制链接发给同事”,而是靠可控的访问通道。SSH 隧道就是那个可控的“门禁卡”,它决定了谁能进、从哪进、进多深。
2. 从零开始:Ubuntu 20.04 上的 Python 3 环境与 Jupyter 安装实录
Ubuntu 20.04 自带 Python 3.8,但直接用系统 Python 装 Jupyter 是条死路。原因很现实:系统包管理器(apt)安装的python3-jupyter版本老旧(2020 年的 4.x),缺jupyter lab,不支持pip install --user的现代工作流,更关键的是——它和你后续要装的pytorch、tensorflow、opencv等科学计算库存在 ABI 冲突。热词里conda create -n pytorch_env python=3.9的出现绝非偶然,这是过来人的血泪教训。
我试过三次:第一次用sudo apt install jupyter-notebook,装完发现pip list | grep jupyter显示的是jupyter-core 4.6.3,而最新版已是5.3.0;第二次用pip3 install jupyter,结果import numpy报ImportError: libopenblas.so.0: cannot open shared object file;第三次干脆删了重来,这次用pyenv管理 Python 版本,pipx安装 Jupyter,终于稳了。下面是我现在固定下来的四步法,每一步都有明确目的,不是为了“看起来高级”,而是为了可复现、可回滚、可协作:
2.1 创建隔离的 Python 运行时环境
不用conda(它太重,启动慢,且热词里anaconda安装jupyter notebook后面跟着一堆ubuntu没声音20.04、搜狗输入法这种无关问题,说明社区对 conda 在桌面 Ubuntu 上的兼容性仍有疑虑),也不用系统 Python,而是用pyenv:
# 安装 pyenv(需先装 build-essential 和 zlib1g-dev) curl https://pyenv.run | bash export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init -)" # 重新加载 shell 配置 source ~/.bashrc # 安装 Python 3.9.18(比热词里的 3.9 更稳定,跳过 3.9.16 的 SSL bug) pyenv install 3.9.18 pyenv global 3.9.18 python --version # 应输出 Python 3.9.18这一步的核心价值是版本锁定。pyenv global不是永久绑定,而是设置当前 shell 的默认 Python。你随时可以用pyenv local 3.10.12切换项目级版本,pyenv shell 3.8.10临时覆盖,完全不影响系统 Python。比conda activate更轻量,比virtualenv更底层。
2.2 用 pipx 安装 Jupyter(而非 pip)
pipx是专为安装“终端应用”设计的工具,它自动为每个应用创建独立虚拟环境,避免pip install jupyter导致的依赖污染:
# 安装 pipx python -m pip install --user pipx python -m pipx ensurepath source ~/.bashrc # 用 pipx 安装 jupyter 和 jupyterlab pipx install jupyter pipx install jupyterlab # 验证 jupyter --version # 输出类似:jupyter core : 5.3.0 jupyter lab --version # 输出类似:4.0.7pipx会把 Jupyter 安装到~/.local/pipx/venvs/jupyter/下,二进制软链接放在~/.local/bin/。这意味着:
jupyter notebook命令全局可用,但它的依赖不会和你的项目requirements.txt冲突;- 升级 Jupyter 只需
pipx upgrade jupyter,不会波及任何 Python 包; - 删除 Jupyter 只需
pipx uninstall jupyter,干净利落,不留残骸。
2.3 初始化 Jupyter 配置并生成密码
Jupyter 默认不设密码,但通过 SSH 隧道访问时,密码是最后一道防线。生成强密码并写入配置:
# 生成配置文件(如果不存在) jupyter notebook --generate-config # 生成哈希密码(注意:这里用的是 Python 3.9 的 hashlib,不是旧版) python3 -c "from notebook.auth import passwd; print(passwd())" # 终端会提示输入密码,例如输入 'mySecurePass123',输出一长串 sha256:... 字符串 # 编辑配置文件 nano ~/.jupyter/jupyter_notebook_config.py在配置文件末尾添加以下内容(逐字复制,不要漏掉任何引号和括号):
# 绑定到所有网络接口(关键!否则 SSH 隧道无法中转) c.NotebookApp.ip = '0.0.0.0' # 指定端口(可选,但建议固定,方便记忆) c.NotebookApp.port = 8888 # 关闭浏览器自动打开(服务器没图形界面) c.NotebookApp.open_browser = False # 设置密码(粘贴上面 python3 -c 命令输出的完整字符串) c.NotebookApp.password = 'sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # 允许远程访问(必须设为 True,否则会报 403 Forbidden) c.NotebookApp.allow_remote_access = True # 禁用 token(token 是临时密钥,不适合长期使用) c.NotebookApp.token = '' # 可选:指定工作目录,避免每次启动都在 home 目录 c.NotebookApp.notebook_dir = '/home/your_username/notebooks'注意:
c.NotebookApp.ip = '0.0.0.0'是 SSH 隧道能工作的前提。很多教程只写localhost,那是本地开发模式;0.0.0.0才表示“监听本机所有网卡”,SSH 隧道才能把流量送进来。但别担心,0.0.0.0本身不等于“开放给公网”,它只是告诉 Jupyter:“准备好接收来自本机任意 IP 的连接”,而 SSH 隧道的流量,在服务器看来,来源 IP 就是127.0.0.1(因为隧道终点在本地 loopback)。这是双重保险。
2.4 启动服务并验证本地访问
配置完成后,启动 Jupyter:
# 后台启动,日志输出到 notebook.log nohup jupyter notebook > ~/notebook.log 2>&1 & # 查看进程是否存活 ps aux | grep jupyter # 查看日志末尾是否有 "The Jupyter Notebook is running at:" 行 tail -20 ~/notebook.log此时,在服务器本机执行:
curl -s http://localhost:8888/api | head -10如果返回 JSON 数据(含"version"字段),说明 Jupyter 已正常监听8888端口。如果报Connection refused,请检查:
jupyter_notebook_config.py中c.NotebookApp.ip是否为'0.0.0.0'(不是'localhost'或'127.0.0.1');c.NotebookApp.port是否与启动命令一致(如你改成了8889,就要用curl http://localhost:8889/api);c.NotebookApp.allow_remote_access是否为True(Ubuntu 20.04 的 Jupyter 5.7+ 强制要求此项)。
这一步成功,意味着你的“投影仪”已经开机、对焦、信号源接入——只差一根“光纤”把它连到你的显示器(本地浏览器)。
3. SSH 隧道的三种实战形态:从基础穿透到免密自动化
很多人卡在ssh -L命令上,不是因为命令写错,而是没理解不同场景下该用哪种形态。我按使用频率和复杂度,把 SSH 隧道分成三类:基础单向隧道、双向保活隧道、免密自动化隧道。它们不是递进关系,而是并列选项,选哪个取决于你的工作流。
3.1 基础单向隧道:一条命令,一次会话
这是最常用、最直观的形态,适合临时调试、快速验证、或在没有持久化需求的 CI/CD 环境中使用:
# 语法:ssh -L [本地IP:]本地端口:远程主机:远程端口 用户名@远程IP ssh -L 8888:localhost:8888 user@192.168.1.100执行后,你将进入远程服务器的 shell。此时在本地电脑浏览器打开http://localhost:8888,即可访问远程 Jupyter。关闭这个 SSH 连接(Ctrl+C或exit),隧道即断开。
关键参数解析:
-L:表示 Local port forwarding(本地端口转发),这是最常用的模式;8888:localhost:8888:左边8888是你本地机器的端口(可任意指定,如8889),右边localhost:8888是远程服务器上的目标地址和端口。这里localhost指的是“远程服务器本机”,不是你的本地电脑;user@192.168.1.100:远程服务器的登录凭证。
提示:热词里
ssh连接reset by peer很可能源于此。如果你的网络不稳定(如 WiFi 切换、休眠唤醒),SSH 连接会中断,隧道随之消失。这不是 Jupyter 的问题,是 TCP 连接本身的脆弱性。解决方案见下文“双向保活隧道”。
3.2 双向保活隧道:永不掉线的稳定通道
基础隧道最大的痛点是“断连即失效”。在笔记本合盖、VPN 切换、地铁穿隧道时,SSH 连接极易被中间设备(路由器、防火墙)静默断开,导致Connection reset by peer。解决方法是启用 SSH 的 KeepAlive 机制,并用autossh实现自动重连:
# 先安装 autossh(Ubuntu 20.04 源里有) sudo apt update && sudo apt install autossh # 启动带保活的隧道(不进入远程 shell,后台运行) autossh -M 0 -N -f -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 8888:localhost:8888 user@192.168.1.100参数详解:
-M 0:禁用 autossh 自带的监控端口(避免端口冲突),改用 SSH 原生心跳;-N:不执行远程命令(只建隧道,不启 shell);-f:后台运行;-o "ServerAliveInterval 30":每 30 秒向服务器发一个空包,维持连接活跃;-o "ServerAliveCountMax 3":连续 3 次发包无响应,则判定连接死亡,autossh 自动重连。
验证隧道是否存活:
# 查看 autossh 进程 ps aux | grep autossh # 查看本地 8888 端口是否被占用 lsof -i :8888 # 本地 curl 测试 curl -s http://localhost:8888/api | head -5这个配置实测可在 WiFi 断连 2 分钟后自动恢复,比手动ssh稳定十倍。ssh连接过段时间自动断开这个热词,答案就在这里。
3.3 免密自动化隧道:一键启动,永久可用
每次输密码太麻烦,尤其当你需要频繁连接时。ssh 免输入密码 vscode、git生成ssh密钥并添加到github这些热词指向同一个解决方案:SSH 密钥对认证。但光有密钥还不够,要让它“一键启动”,需要结合 systemd 用户服务:
# 1. 生成密钥对(如尚无) ssh-keygen -t ed25519 -C "your_email@example.com" # 2. 复制公钥到服务器(自动追加到 ~/.ssh/authorized_keys) ssh-copy-id user@192.168.1.100 # 3. 测试免密登录 ssh -o "ConnectTimeout=5" user@192.168.1.100 "echo OK" # 应输出 OK # 4. 创建 systemd 用户服务(本地电脑执行,非服务器!) mkdir -p ~/.config/systemd/user/ nano ~/.config/systemd/user/jupyter-tunnel.service写入以下内容(替换user和192.168.1.100):
[Unit] Description=Jupyter SSH Tunnel to Remote Server After=network.target [Service] Type=simple ExecStart=/usr/bin/autossh -M 0 -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 8888:localhost:8888 user@192.168.1.100 Restart=always RestartSec=10 User=%i [Install] WantedBy=default.target启用并启动服务:
# 重载配置 systemctl --user daemon-reload # 开机自启(登录时自动启动) systemctl --user enable jupyter-tunnel.service # 立即启动 systemctl --user start jupyter-tunnel.service # 查看状态 systemctl --user status jupyter-tunnel.service从此,只要你本地电脑开机、联网、登录用户,http://localhost:8888就永远可用。vscode的jupyter notebook怎么显示大纲、jupyter notebook 使用方法这些操作,再也不会被“连不上”打断。这就是工程化思维:把重复劳动变成一次配置,把人为操作变成系统服务。
注意:
systemctl --user依赖于dbus-user-session,Ubuntu 20.04 默认已启用。如遇Failed to connect to bus错误,请确保你以图形界面用户登录,而非ssh进去后再执行。
4. 故障排查全景图:从Connection refused到403 Forbidden的逐层解剖
即使严格按照上述步骤操作,仍可能遇到各种报错。我整理了一份基于真实日志的故障树,覆盖 95% 的常见问题。排查逻辑遵循“由外到内、由网络到应用”的顺序,每一步都有可执行的验证命令。
4.1 第一层:本地网络与端口可达性
现象:浏览器访问http://localhost:8888显示This site can’t be reached或ERR_CONNECTION_REFUSED。
排查链路:
| 步骤 | 命令 | 预期输出 | 说明 |
|---|---|---|---|
| 1. 本地端口是否被占用 | lsof -i :8888或netstat -tuln | grep :8888 | 应显示autossh或ssh进程监听127.0.0.1:8888 | 如果无输出,说明隧道未建立或已崩溃 |
| 2. 本地能否连通远程服务器 | ping -c 3 192.168.1.100 | 3 packets transmitted, 3 received | 网络层不通,检查 IP、子网、防火墙 |
| 3. 本地能否 SSH 登录 | ssh -o "ConnectTimeout=5" user@192.168.1.100 "echo test" | 输出test | SSH 层不通,检查密钥、密码、sshd 配置 |
提示:
ssh: could not resolve hostname d: name or service not known这类错误,99% 是因为你把d当成了主机名,实际应是d.example.com或 IP。检查你的ssh命令,hostname 参数是否拼写错误。
4.2 第二层:SSH 隧道与远程端口映射
现象:本地lsof显示端口被占用,但curl http://localhost:8888仍失败。
排查链路:
| 步骤 | 命令 | 预期输出 | 说明 |
|---|---|---|---|
| 1. 隧道进程是否存活 | ps aux | grep autossh | 应显示 autossh 进程,且State为S(sleeping) | 如为Z(zombie),需重启服务 |
| 2. 远程服务器上 Jupyter 是否监听 | ssh user@192.168.1.100 "netstat -tuln | grep :8888" | 应显示tcp6 0 0 :::8888 :::* LISTEN | 如果是127.0.0.1:8888,说明jupyter_notebook_config.py中c.NotebookApp.ip设错了 |
| 3. 远程服务器上能否本地访问 | ssh user@192.168.1.100 "curl -s http://localhost:8888/api | head -5" | 返回 JSON,含"version"字段 | 如果失败,说明 Jupyter 服务未启动或配置错误 |
4.3 第三层:Jupyter 应用层配置与认证
现象:curl能拿到 JSON,但浏览器访问显示403 : Forbidden或401 : Unauthorized。
排查链路:
| 步骤 | 检查点 | 正确值 | 说明 |
|---|---|---|---|
1.c.NotebookApp.password | 配置文件中该行是否取消注释?字符串是否完整粘贴? | c.NotebookApp.password = 'sha256:...' | 常见错误:只粘贴了sha256:后面部分,漏了引号 |
2.c.NotebookApp.allow_remote_access | 是否设为True? | c.NotebookApp.allow_remote_access = True | Ubuntu 20.04 的 Jupyter 5.7+ 默认为False,必须显式开启 |
3.c.NotebookApp.token | 是否设为空字符串? | c.NotebookApp.token = '' | 如果留空或注释掉,Jupyter 会生成随机 token,而 SSH 隧道无法传递它 |
| 4. 浏览器缓存 | 是否清空了localhost:8888的 Cookie? | 访问时不应弹出 token 输入框 | Jupyter 会记住上次登录状态,缓存可能导致 403 |
4.4 第四层:Ubuntu 20.04 特定陷阱与绕过方案
Ubuntu 20.04 有几个独有的坑,其他发行版没有,必须单独处理:
陷阱 1:systemd-resolved导致 DNS 解析失败
现象:ssh user@myserver.local失败,报Could not resolve hostname,但ping myserver.local成功。
原因:systemd-resolved的 stub resolver 与某些 SSH 配置冲突。
绕过:在/etc/systemd/resolved.conf中设置DNSStubListener=no,然后sudo systemctl restart systemd-resolved。
陷阱 2:ufw防火墙默认拒绝所有入站
现象:ssh登录成功,但netstat看不到8888端口监听。
原因:ufw默认策略是deny incoming。
绕过:sudo ufw allow from 127.0.0.1 to any port 8888(只允许本地回环访问,符合安全原则)。
陷阱 3:jupyter notebook 中文符号输入要输入两次
现象:在 Jupyter cell 中输入中文标点(如,。!?),需按两次才显示。
原因:Ubuntu 20.04 的 IBus 输入法与 Chromium 内核的 Webview 兼容性问题。
绕过:在 Jupyter 启动时加参数--no-sandbox(不推荐)或改用 Firefox 浏览器访问http://localhost:8888。
这些细节,文档里不会写,但你在 Ubuntu 20.04 上踩过一次,就会记一辈子。
5. 进阶实践:VS Code 远程开发与 Jupyter 的无缝协同
当 SSH 隧道稳定运行后,下一步自然是要把它融入日常开发流。vscode连接ssh远程服务器、vscode ssh、remote ssh这些热词,本质都是在问:“如何让 VS Code 直接操作远程文件,并在远程内核上运行 Notebook?”答案不是“用 VS Code 打开远程.ipynb文件”,而是“让 VS Code 的 Jupyter 扩展,通过已有的 SSH 隧道,连接到远程 Jupyter 服务”。
5.1 VS Code 远程资源管理器:文件即刻同步
安装 VS Code 官方扩展Remote - SSH。配置连接:
Ctrl+Shift+P→Remote-SSH: Connect to Host...→Add New SSH Host...- 输入:
ssh -L 8888:localhost:8888 user@192.168.1.100(注意,这里不是直接写user@ip,而是把隧道命令作为 Host 配置) - 选择配置文件位置(推荐
~/.ssh/config),VS Code 会自动生成如下内容:
Host my-jupyter-server HostName 192.168.1.100 User user LocalForward 8888 localhost:8888保存后,点击侧边栏Remote Explorer→my-jupyter-server→Connect。VS Code 将在远程服务器上安装 server,之后所有文件操作(打开、编辑、保存)都实时作用于远程磁盘。你不再需要scp、rsync或git push/pull来同步代码。
5.2 VS Code Jupyter 扩展:内核直连,无需导出
安装扩展Jupyter(Microsoft 官方)。打开一个.ipynb文件后:
- 点击右上角
Select Kernel→Existing Jupyter Server→Enter URL - 输入
http://localhost:8888(注意:是localhost,不是服务器 IP!因为隧道已把远程8888映射到本地8888) - 输入你在
jupyter_notebook_config.py中设置的密码
此时,VS Code 的 Notebook 编辑器将完全接管远程 Jupyter 内核。你可以:
- 在 cell 中写
!ls,执行的是远程服务器的 shell 命令; import torch,加载的是远程服务器上conda create -n pytorch_env python=3.9创建的环境;plt.show(),图表直接渲染在 VS Code 内置 WebView 中,无需matplotlib inline。
提示:
vscode的jupyter notebook怎么显示大纲?大纲(Outline)功能依赖于 VS Code 对.ipynb文件的解析。只要文件格式正确(JSON 结构合法),大纲会自动出现在侧边栏。如果没显示,按Ctrl+Shift+P→Developer: Toggle Developer Tools,看 Console 是否有outline provider相关错误。
5.3 从 Notebook 到 Production:一键导出可部署脚本
Jupyter 是探索工具,不是生产环境。热词里jupyter notebook 深度学习后往往跟着vins mono ubuntu 20.04、mysql8.025这类部署需求。这时,你需要把 Notebook “翻译”成可维护的 Python 脚本:
# 将 notebook 导出为 .py 脚本(保留所有代码 cell,忽略 markdown) jupyter nbconvert --to python my_analysis.ipynb # 导出为带执行结果的 HTML(用于汇报) jupyter nbconvert --to html --no-input my_analysis.ipynb # 导出为 PDF(需安装 pandoc 和 texlive) jupyter nbconvert --to pdf my_analysis.ipynb生成的my_analysis.py可直接用python3 my_analysis.py运行,也可作为模块被其他脚本导入。这才是数据科学工作流的终点:从交互式探索,到自动化 pipeline。
我实际工作中,所有模型训练代码都先在 Jupyter 里调通,然后nbconvert成.py,再用black格式化、pylint检查,最后提交到 Git。Jupyter 只是草稿纸,.py才是正式交付物。
6. 安全边界与运维守则:为什么不能把 Jupyter 直接暴露在公网
最后,必须强调一个原则:SSH 隧道是手段,不是目的;它的存在,恰恰是为了证明——你不应该、也不需要把 Jupyter 暴露在公网。热词里jupyter notebook怎么分享、jupyter notebook 使用方法后面,藏着无数人试图用nginx + ssl或jupyterhub搭建公开服务的失败案例。我见过太多因为c.NotebookApp.ip = '0.0.0.0'配错,导致服务器被挖矿木马入侵的事故。
6.1 SSH 隧道的安全优势量化分析
| 攻击面 | 直接暴露 Jupyter | SSH 隧道方案 | 说明 |
|---|---|---|---|
| 认证方式 | 仅靠 Jupyter 密码(易暴力破解) | SSH 密钥 + Jupyter 密码(双因子) | SSH 密钥强度远超人工密码,且可设ssh -o "PasswordAuthentication=no"强制禁用密码登录 |
| 传输加密 | 依赖 HTTPS(需额外配 TLS 证书) | SSH 协议原生 AES-256 加密 | 无需证书管理,无SSL certificate verify failed报错 |
| 访问控制 | 依赖ufw或云安全组(粒度粗) | 依赖~/.ssh/authorized_keys(精确到公钥) | 一个密钥对应一个用户,吊销只需删一行 |
| 日志审计 | Jupyter 日志只记录 HTTP 请求 | SSH 日志记录完整登录时间、IP、端口、命令 | /var/log/auth.log是最权威的审计源 |
6.2 四条不可逾越的运维红线
绝不修改
c.NotebookApp.ip为公网 IPc.NotebookApp.ip = '192.168.1.100'是自杀行为。0.0.0.0是安全的,因为它只表示“监听本机所有接口”,而 SSH 隧道的流量来源始终是127.0.0.1。一旦写成具体 IP,就等于告诉系统:“允许来自这个 IP 的任何连接”,防火墙规则将形同虚设。绝不关闭
c.NotebookApp.allow_remote_access以外的防护
有人为“省事”设c.NotebookApp.disable_check_xsrf = True,这是重大安全漏洞。XSRF(跨站请求伪造)保护必须开启,它是防止恶意网站诱导用户执行危险操作的最后一道闸门。绝不共享
.jupyter目录或jupyter_notebook_config.py
该文件包含密码哈希,一旦泄露,攻击者可离线爆破。正确的做法是:在团队内分发一份空白模板,每人用自己的passwd()生成密码。绝不让 Jupyter 以 root 用户运行
sudo jupyter notebook是禁忌。Jupyter 进程应以普通用户身份启动,其工作目录(notebook_dir)也应属于该用户。这样即使内核被攻破,攻击者也无法写入/etc或/root。
这些不是教条,而是用服务器被黑、数据被勒索的代价换来的经验。当你看到jupyter notebook 安装这个热词时,请记住:安装只是 1%,安全配置才是那 99%。
我在 Ubuntu 20.04 上跑了三年 Jupyter,从没出过安全事故,靠的不是运气,而是这四条红线划出来的安全区。技术可以炫酷,但生产环境的第一守则是——活着。