Debian 12 上用 TPM2.0 安全登陆 SSH

需求

之前在《macOS 上用触控 ID 安全登录 SSH》,最近比较常用 Linux 桌面,所以还得在 Linux 上也搞一套安全的登陆方式。

分析

目前,MBA13 M1 的 Touch ID 在 Asahi Linux 下无法使用,其它机器也都没有指纹识别器,显然只有 TPM 2.0 模块是唯一适合的安全手段。

参考

实现

1. 检查 TPM

1
sudo dmesg | grep -i tpm

如果没有输出,那可能是内核不支持或者没有 TPM 芯片;但也可能只是内核比较老,没有输出而已,比如华为擎云 W515 就没有输出,但它能用。

类似以下输出,是没有芯片:

1
2
[    0.800622] ima: No TPM chip found, activating TPM-bypass!
[ 2.030844] systemd[1]: systemd 252.22-1~deb12u1 running in system mode (+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified)

内核识别出 TPM 芯片的输出:

1
2
3
4
[    0.000000] efi: ACPI=0x75620000 ACPI 2.0=0x75620014 TPMFinalLog=0x755ef000 SMBIOS=0x75cac000 SMBIOS 3.0=0x75cab000 MEMATTR=0x6bf5d018 ESRT=0x6e822718 MOKvar=0x75ce8000
[ 0.014309] ACPI: TPM2 0x00000000754DF000 00004C (v04 ALASKA A M I 00000001 AMI 00000000)
[ 0.014336] ACPI: Reserving TPM2 table memory at [mem 0x754df000-0x754df04b]
[ 12.590389] systemd[1]: systemd 252.22-1~deb12u1 running in system mode (+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT +QRENCODE +TP2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified)

2. 安装必要软件

1
sudo apt install tpm2-tools tpm2-abrmd libtpm2-pkcs11-tools libtpm2-pkcs11-1

其中:

  • tpm2-tools 是一套工具。获得 tpm2tss2 两个命令,可以用 sudo tpm2 getrandom --hex 8 测试 TPM2 是否正常工作。

  • tpm2-abrmd 是一个服务,使得非 root 用户能访问 TPM2 映射文件,只需把用户加到 tss 组即可。

  • libtpm2-pkcs11-tools 是 PKCS#11 backend,用于访问加密服务。它提供的 tpm2_ptool 是一个 Python 脚本,可用于管理令牌、密钥。

  • libtpm2-pkcs11-1 是 SSH 用的模块,即提供一个符合 PKCS#11 规范的 so 动态库。对于 x64 机器,固定链接位于 /lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so。如果您安装了 gcc,则可用 TPM2_PKCS11_SO=/usr/lib/$(gcc -dumpmachine)/pkcs11/libtpm2_pkcs11.so 来保存它到变量 TPM2_PKCS11_SO,以方便使用。

3. 配置用户

1
sudo usermod -a -G tss $USER

如果在桌面或 SSH 会话,可以注销,再登录,使以上配置生效。也可以用 su - $USER 登录一个新会话,这时新会话是生效的,但 exit 后,回到的旧会话依然不生效。

4. 检验

1
tpm2 getrandom --hex 8

注意,上面这条没有 sudo,必须保证没有 sudo 也能获得随机数,才说明 tpm2-abrmd 正常工作,并且用户加入 tss 组,并且重新登录会话!

5. 新建私钥

1
2
3
4
5
6
7
8
# 在默认位置 ~/.tpm2_pkcs11 初始化一个数据库
tpm2_ptool init

tpm2_ptool addtoken --pid 1 --label SSHToken --sopin [SUPERVISOR-PIN] \
--userpin [USER-PIN]

tpm2_ptool addkey --algorithm [rsa2048/ecc256/ecc384] --label SSHToken \
--key-label [KEY-LABEL] --userpin [USER-PIN]

稣一般使用 ecc384,即 NIST P-384,但有些机器会报错,这时可以尝试 ecc256,即 NIST P-256。注意:据说 NIST 系列安全性存疑?

6. 导入私钥

这第 6 步与第 5 步只需二选一。

第 5 步中新建私钥是无法导出的,万一自己不小心删了,或者升级 BIOS,也可能无法解开,导致私钥丢失。所以,如果这私钥是不能丢的,那最好的方式是从离线保存的私钥文件里导入。

这步可能有风险,因为需要把离线保存的私钥文件复制到目标机器,而且要将私钥的密码清零。为了提高安全性,建议离线操作,并尽可能快速完成全部流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 在内存文件系统操作,防止私钥落盘
umask 077 && mkdir /tmp/crypto && cd /tmp/crypto

# 这里断网,再复制你的私钥到 /tmp/crypto/tpm_pri
# 假设是从 U 盘复制,那命令可能是:
sudo mount -o ro /dev/sdb1 /mnt
cp /mnt/key.pem tpm_pri

# 解密私钥,即删除私钥的密码(这很危险!平时绝不能这样做!)
ssh-keygen -f tpm_pri -mPEM -ep

# 导入私钥
tpm2_ptool import --label SSHToken --key-label TPM-SSH --userpin [USER-PIN] \
--privkey /tmp/crypto/tpm_pri --algorithm ecc384

# 清理机密数据
shred -zu tpm_pri
rm -rf /tmp/crypto

7. 查看公钥

1
2
3
TPM2_PKCS11_SO=/usr/lib/$(gcc -dumpmachine)/pkcs11/libtpm2_pkcs11.so

ssh-keygen -D $TPM2_PKCS11_SO

可以导到文件里:

1
ssh-keygen -D $TPM2_PKCS11_SO > ~/.ssh/tpm.pub

8. 设置 SSH 服务端

这里在本地测试:

1
cat ~/.ssh/tpm.pub >> ~/.ssh/authorized_keys2

测试连接:

1
ssh -I $TPM2_PKCS11_SO $USER@localhost

9. 【可选】使用 ssh-agent

1
2
pgrep -u $UID ssh-agent || eval `ssh-agent`
ssh-add -s $TPM2_PKCS11_SO

10. 【可选】对指定主机自动使用 TPM 密钥

添加以下文本:

1
2
3
Host umu618.com
PKCS11Provider /lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so
IdentityAgent none

或者对全部主机使用:

1
2
cat <(echo "PKCS11Provider $TPM2_PKCS11_SO") ~/.ssh/config \
| tee ~/.ssh/config