Securing Ziti Identities with HSM/TPM

Follow into
Save into

Regular readers of this blog know that OpenZiti provides secure overlay networking between Ziti identities. You can improve security of your OpenZiti edge identities by using hardware-based private keys. This guide provides step-by-step instructions on integrating hardware security with OpenZiti. It uses Linux built-in TPM as a hardware security device. Similar steps will also work with other HSM devices.
This is How I (en)Roll
On my Linux(Ubuntu) laptop I check that the kernel detected TPM hardware and created a device for it.
1ziggy@hermes:~$ ls -l /dev/tpm*
2crw-rw---- 1 tss root 10, 224 Apr 26 14:41 /dev/tpm0
3crw-rw---- 1 tss tss 253, 65536 Apr 26 14:41 /dev/tpmrm0
Device /dev/tpmrm0 is there, so I can proceed.
Add my user to the tss group so that the TPM device can be accessed. You will probably need to restart your login shell for that to take effect, check your groups with id command.
1ziggy@hermes:~$ sudo usermod -G tss -a ziggy
Install some useful software packages to interact with the TPM device
libtpm2-pkcs11-tools- includestmp2_ptoolto initialize PKCS#11 token on TPM devicelibtpm2-pkcs11-1- TPM PKCS#11 libraryopensc- includespkcs11-toolfor interacting with PKCS#11 token. This is not needed for OpenZiti enrollment but is useful for inspecting tokens
1ziggy@hermes:~$ sudo apt install libtpm2-pkcs11-tools libtpm2-pkcs11-1 opensc
Now that I have access to the device and the needed software I can initialize the token and test PKCS#11 driver access to it
1ziggy@hermes:~$ tpm2_ptool init
2action: Created
3id: 1
4
5ziggy@hermes:~$ tpm2_ptool addtoken --pid 1 --sopin 1111 --userpin ziggy1 --label ziggy-tpm
6
7ziggy@hermes:~/ziti$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so --list-slots
8WARNING: Getting tokens from fapi backend failed.
9Available slots:
10Slot 0 (0x1): ziggy-tpm
11 token label : ziggy-tpm
12 token manufacturer : Infineon
13 token model : SLB9665
14 token flags : login required, rng, token initialized, PIN initialized
15 hardware version : 1.16
16 firmware version : 5.62
17 serial num : 0000000000000000
18 pin min/max : 0/128
19Slot 1 (0x2):
20 token state: uninitialized
Note: fapi backend warning can be ignored.
To enroll I need to get an enrollment token from my Ziti controller. Once I have that I can enroll. Let’s enroll with ziti-edge-tunnel
1
2# enroll identity with ziti-edge-tunnel
3ziggy@hermes:~$ ziti-edge-tunnel enroll -j ./ziggy.jwt -i ziggy.json -k 'pkcs11:///usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so?label=ziggy-key&pin=ziggy1'
4(1676722)[ 0.000] INFO ziti-sdk:utils.c:198 ziti_log_set_level() set log level: root=3/INFO
5(1676722)[ 0.000] INFO ziti-sdk:utils.c:167 ziti_log_init() Ziti C SDK version 1.5.0 @ga39db85(HEAD) starting at (2025-02-24T15:21:31.292)
6(1676722)[ 0.000] INFO ziti-sdk:ziti_enroll.c:112 ziti_enroll() Ziti C SDK version 1.5.0 @ga39db85(HEAD) starting enrollment at (2025-02-24T15:21:31.294)
7(1676722)[ 0.000] INFO ziti-sdk:ziti_enroll.c:221 start_enrollment() pkcs11 key not found. trying to generate
ZIti identity is stores in the JSON file -- ziggy.json in my case. Let take a look
1{
2 "ztAPI": "https://<my-controller-address>:443",
3 "id": {
4 "cert": "-----BEGIN CERTIFICATE-----....",
5 "key": "pkcs11:///usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so?label=ziggy-key&pin=ziggy1",
6 "ca": "-----BEGIN CERTIFICATE-----...."
7 }
8}
As you can see the .id.key is referencing the private key stored in my laptop's TPM hardware. I can inspect it using pkcs11-tool
1ziggy@hermes:~$ pkcs11-tool --module /usr/lib/x86_64-linux-gnu/pkcs11/libtpm2_pkcs11.so --list-objects --pin ziggy1 1 ↵
2Using slot 0 with a present token (0x1)
3Public Key Object; EC EC_POINT 384 bits
4 EC_POINT: 04610442fa885e120e9c63227aa7c53aeb84a52400264f3452fcf4dc4dbf4ade9ca46e7e15c4d37cc7a317edd860887ccd5746eea70cdea1d106b36b59297ecb93d43c57f4e8aef7029fa55f52cd740292d8de92fbce35c7788d3cc00f528cac2d0e4d
5 EC_PARAMS: 06052b81040022 (OID 1.3.132.0.34)
6 label: ziggy-key
7 ID: dad9d0f20cc1d9ec
8 Usage: encrypt, verify, wrap
9 Access: local
10Private Key Object; EC
11 label: ziggy-key
12 ID: dad9d0f20cc1d9ec
13 Usage: decrypt, sign, unwrap
14 Access: sensitive, always sensitive, never extractable, local
15 Allowed mechanisms: ECDSA,ECDSA-SHA1,ECDSA-SHA256,ECDSA-SHA384,ECDSA-SHA512
As you can see my TPM token ziggy-tpm now has two objects private, and public keys with same label and id. OpenZiti SDK will use that hardware key to provide identity.
Now I can start ziti-edge-tunnel with that identity:
1ziggy@hermes:~$ sudo -E ziti-edge-tunnel run -i ziggy.json
Note: -E flag is required since I initialized TPM token as user ziggy and there are some support files created in the user’s $HOME directory.