保护访问安全
一台随处可达、还会执行 AI 生成代码的机器:安全不是可选项。SSH 密钥、防火墙、密钥、代理权限。如何不胡来。
本文目录
本文核对于 3 个月前,部分命令可能已有变化。 如有出入,请告诉我们。
要点速览
要保护一台随处可达、由 AI 代理操控的迷你 PC:SSH 只用密钥(关掉密码登录、禁止 root 登录),ufw 防火墙默认全部拒绝,不把任何裸服务暴露到互联网上(私有走 Tailscale,公开走 Cloudflare Tunnel),密钥放在 git 之外、权限设为 chmod 600 的 .env 文件里。代理以您的普通用户身份、在项目目录里运行,不自动 sudo,并开启它的权限规则或沙箱。再加上自动安全更新,以及真正测试过恢复的备份。
请先完成: 安装 agent(趁早)
回顾一下您搭起来的东西:一台 7×24 运行、能从手机访问、把服务暴露到互联网上、而且会执行 AI 提议的命令的机器。这很棒。但它同时也是一片需要认真对待的攻击面,不是出于偏执,而是出于卫生习惯。
好消息是:90% 的安全只需几个简单的动作,做一次就行。这篇就是这些动作。没有一个是复杂的。可一旦跳过,反而可能把您的工坊变成垃圾邮件中转站,甚至更糟。
1. SSH:用密钥,永远别用密码
远程访问走的是 SSH。一个暴露在外的 SSH 密码,是互联网上头号攻击目标,机器人每天测试上百万种组合。对策一劳永逸:关掉密码认证,改用密钥。
一把 SSH 密钥是一对:私钥留在您的笔记本上(绝不放别处),公钥放到机器上。没有私钥,谁也进不来,哪怕知道您的用户名也没用。
已完成 0 步,共 3 步 勾选记录只保存在此浏览器中。
-
在您的电脑上生成密钥(不是在 mini-PC 上)
# 在您的笔记本上。Ed25519 = 现代、短小、可靠。 ssh-keygen -t ed25519 -C "laptop-vers-minipc"路径保持默认,设一个口令(passphrase)(万一笔记本被偷,这是最后一道屏障)。
-
把公钥放到机器上
ssh-copy-id ulrich@adresse-de-la-machine测试连接:
ssh ulrich@adresse-de-la-machine应该不问密码就进去。 -
关掉密码
在 mini-PC 上,编辑
/etc/ssh/sshd_config(您的代理可以做,告诉它您要什么):PasswordAuthentication no PermitRootLogin no然后重载:
sudo systemctl restart ssh。在确认新的密钥连接能用之前,留一个已打开的会话,以防万一。有个坑:在 Ubuntu 上,
sshd_config一开头就会包含/etc/ssh/sshd_config.d/里的文件,而且先读到的值生效。放在那里的某个文件,比如 Ubuntu Server 安装后有时留下的50-cloud-init.conf,就可能悄悄把密码登录重新打开。检查一下实际生效的配置:# 应显示 passwordauthentication no 和 permitrootlogin no sudo sshd -T | grep -Ei '^(passwordauthentication|permitrootlogin) '
2. 防火墙:全部关闭,只留必需的
默认情况下,我们拦下所有进来的流量,再一点一点地放行。ufw 让这件事变得轻而易举。
sudo ufw default deny incoming # 默认拒绝一切
sudo ufw default allow outgoing # 机器可以往外连
sudo ufw allow 22/tcp # SSH(如果走 Tailscale 就什么都不开)
sudo ufw enable
sudo ufw status verbose # 检查一下
两条命令看清自己的处境:
# 哪些服务监听在所有网卡上,也就是机器之外能访问到的
sudo ss -ltnp | grep -E "0\.0\.0\.0:|\[::\]:"
# ufw 真的在过滤 IPv6 吗?(应当输出 IPV6=yes)
grep IPV6 /etc/default/ufw
3. 密钥:永远不明文,永远不进代码
API 密钥、令牌、服务密码:这些都是密钥(secrets)。规则是绝对的:密钥绝不以明文形式存在于代码中,也绝不进入被 git 版本管理的文件。
已完成 0 步,共 3 步 勾选记录只保存在此浏览器中。
-
把密钥放进一个 git 之外的 .env 文件
# 项目根目录下的一个 .env 文件 echo "ANTHROPIC_API_KEY=sk-..." >> .env # 而且最重要的是,把它排除在 git 之外 echo ".env" >> .gitignore代码读取环境变量,它从不包含具体的值。
-
锁死敏感文件的权限
chmod 600 .env ~/.ssh/id_ed25519 # 只有您能读 -
对泄露过的东西做轮换
如果某个密钥曾经留在某处(一次提交、一次复制粘贴、一张截图),就把它当成已泄露,并重新生成它。一个 API 密钥点两下就能吊销并重建。白白轮换一次,也好过对泄露视而不见。
您的密钥到底该存哪里
.env 文件足够起步,但一旦您攒下越来越多密钥(AI 的 API、GitHub 令牌、各种服务的访问权),就该问问自己什么才是合适的保险箱。这里有个从简到强的小层级:
- 每个项目一个
.env文件,起点。放在 git 之外,chmod 600。对个人项目来说很完美。它的局限:密钥明文躺在磁盘上,而且会从一个项目复制到另一个项目。 - 钥匙串 / 密码管理器,给您个人的密钥储备(也就是真相之源),一个真正的管理器(Bitwarden、1Password、KeePassXC……)加密保存,胜过一千个散落各处的文本文件。需要时您从里面取出来填进
.env。 - 静态加密的保险箱,为了把密钥存进仓库而不暴露它们,像
sops+age或pass这样的工具会加密这些值:文件可以版本化,只有持有解密密钥的人才能读。当项目变大或要共享时,这是专业的一步。 - 专门的密钥管理服务,面向认真的多机场景(Vault、Infisical、Doppler……),由一个服务集中管理、审计并轮换密钥。一开始大概超出您的需要,但路最终是通向这里的。
4. 给代理划界限:它执行,您决定
这是我们这套配置特有、也最重要的部分。一个编码代理之所以强大,正因为它能运行命令。这也正是它在无人看管时变危险的原因。几条常识规则:
- 绝不让代理跑在
root下,绝不自动sudo。 它在您的普通用户下运行。当一条命令需要sudo时,您要亲眼看到并手动批准它。一条在 root 下运行的破坏性命令是不留情面的。 - 「自动批准」模式得靠争取。 代理常常提供一种不问您就执行的模式。用来迭代测试很棒;但凡涉及网络、密钥、删文件(
rm)或生产环境的,一律禁止。 - 在项目文件夹里干活,别在
~或/里。 把代理的作用半径限制在项目文件夹内。万一它跑偏,损害也被困在里面。 - 生成的代码在上生产前要重读一遍。 一个代理可能无意间引入漏洞(一条转义不当的 SQL 查询、一个过宽的权限)。重读一遍,由您,或由第二个充当「评审」的代理,不是奢侈。
- git 是您的安全网。 频繁提交。如果代理干了蠢事,您总能回退。一个没有 git 历史的项目,就是没有安全网的杂技演员。
5. 让机器保持更新(不用操心)
一个被修复的漏洞就不再是漏洞了,前提是装上补丁。我们把安全更新自动化:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades # 回答「是」
机器从此自己安装安全补丁。至于其余的(版本升级),时不时来一次 sudo apt update && sudo apt upgrade 就够了,您的代理可以提醒您。
6. 安全网:备份
安全不只是拦住入侵者。也是要扛过您自己的失误、一块罢工的硬盘、一个太热心的代理。把重要的东西(您的项目、您的配置、您那些 .env,加密后)备份到机器之外:另一块硬盘、一个 NAS、一处远程存储。一个您从没试着恢复过的备份,不算备份。
本文全部命令
常见问题
我已经关闭了密码登录,为什么 SSH 还接受密码?
在 Ubuntu 上,sshd_config 一开头就会引入 /etc/ssh/sshd_config.d/ 目录下的文件,而且先读到的值优先生效。放在那里的某个文件,比如 Ubuntu Server 安装时有时会留下的 50-cloud-init.conf,就可能在您不知情时重新启用密码登录。请用 sudo sshd -T 检查实际生效的配置,结果应显示 passwordauthentication no 和 permitrootlogin no。
ufw 防火墙能保护 Docker 容器吗?
不能。用 -p 8080:8080 发布端口时,Docker 会把自己的规则写在 ufw 的规则之前,即使防火墙开着,整个局域网也都能访问这个容器。解决办法在于监听地址:改用 -p 127.0.0.1:8080:8080,服务就只能从本机访问。
我的迷你 PC 会通过 IPv6 暴露在互联网上吗?
有可能。IPv6 没有 NAT:机器拥有自己的公网地址,监听 [::] 的服务可以直接从互联网访问。这时保护您的是路由器的 IPv6 防火墙,大多数型号默认开启。也请确认 ufw 会过滤 IPv6:/etc/default/ufw 文件里应该是 IPV6=yes。
密钥越来越多,应该放在哪里?
起步阶段,每个项目一个 .env 文件就够了,放在 git 之外,权限设为 chmod 600。您个人的密钥库,可以用加密的密码管理器作为唯一可信来源,例如 Bitwarden、1Password 或 KeePassXC。要把密钥放进代码仓库做版本管理,可以用 sops 配合 age,或者用 pass 加密;跨多台机器时,Vault、Infisical 或 Doppler 这类专用密钥管理服务可以集中管理并定期轮换密钥。
可以把 API 密钥直接粘贴到对话里交给代理吗?
最好不要:在对话里传过的密钥,就是又一个需要盯着的密钥。更好的做法是教代理从环境变量里读取密钥,并在 CLAUDE.md 或 AGENTS.md 里写一条规则,要求它永远不以明文写出密钥。如果某个密钥已经在别处留下痕迹,就当它已经泄露,立即重新生成。
本文涉及的术语: SSHAPIGitCommit(提交)
发现错误?
命令失效了,价格变了?
这些工具每个月都在变。请告诉我这篇文章哪里不对,我会改正并更新日期。