跳到正文
全部 31 篇FREN中文
目录
第 3 部分 · 第 2 篇,共 4 篇 难度:硬核 阅读时间:20 分钟 适用平台:Linux和Mac

保护访问安全

一台随处可达、还会执行 AI 生成代码的机器:安全不是可选项。SSH 密钥、防火墙、密钥、代理权限。如何不胡来。

本文目录
  1. 011. SSH:用密钥,永远别用密码
  2. 022. 防火墙:全部关闭,只留必需的
  3. 033. 密钥:永远不明文,永远不进代码
  4. 044. 给代理划界限:它执行,您决定
  5. 055. 让机器保持更新(不用操心)
  6. 066. 安全网:备份
  7. 07常见问题

要点速览

要保护一台随处可达、由 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 步 勾选记录只保存在此浏览器中。

  1. 在您的电脑上生成密钥(不是在 mini-PC 上)

    # 在您的笔记本上。Ed25519 = 现代、短小、可靠。
    ssh-keygen -t ed25519 -C "laptop-vers-minipc"
    

    路径保持默认,设一个口令(passphrase)(万一笔记本被偷,这是最后一道屏障)。

  2. 把公钥放到机器上

    ssh-copy-id ulrich@adresse-de-la-machine
    

    测试连接:ssh ulrich@adresse-de-la-machine 应该不问密码就进去。

  3. 关掉密码

    在 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 步 勾选记录只保存在此浏览器中。

  1. 把密钥放进一个 git 之外的 .env 文件

    # 项目根目录下的一个 .env 文件
    echo "ANTHROPIC_API_KEY=sk-..." >> .env
    # 而且最重要的是,把它排除在 git 之外
    echo ".env" >> .gitignore
    

    代码读取环境变量,它从不包含具体的值。

  2. 锁死敏感文件的权限

    chmod 600 .env ~/.ssh/id_ed25519   # 只有您能读
    
  3. 对泄露过的东西做轮换

    如果某个密钥曾经留在某处(一次提交、一次复制粘贴、一张截图),就把它当成已泄露,并重新生成它。一个 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(提交)

发现错误?

命令失效了,价格变了?

这些工具每个月都在变。请告诉我这篇文章哪里不对,我会改正并更新日期。

只保留页面、您的留言和选填的联系方式,别无其他。

第 16 / 31 篇 · 第 3 部分 还没有读过任何一篇 打开文章目录