|
| 1 | +# How to Set Up TLS for NVMe-TCP |
| 2 | + |
| 3 | +Enabling TLS for the NVMe-TCP feature requires a few configuration steps for both the kernel and userland. |
| 4 | + |
| 5 | +## Kernel Configuration |
| 6 | + |
| 7 | +To support TCP authentication and TLS encryption, enable the following kernel options: |
| 8 | + |
| 9 | +- For DHCHAP authentication: |
| 10 | + `CONFIG_NVME_HOST_AUTH` |
| 11 | + |
| 12 | +- For TLS transport encryption: |
| 13 | + `CONFIG_NVME_TCP_TLS` |
| 14 | + |
| 15 | +## Setting Up `tlshd` |
| 16 | + |
| 17 | +For TLS protocol support, which handles authentication and encryption, the kernel handles data encryption only, so userland support is required for the TLS handshake. The `tlshd` daemon implements the handshake process. |
| 18 | + |
| 19 | +### Requirements |
| 20 | + |
| 21 | +Ensure `tlshd` includes the commit `311d9438b984` ("tlshd: always link .nvme default keyring into the session") - likely in `ktls-utils` version 0.12. Alternatively, you can set the keyring manually in `/etc/tlshd.conf`: |
| 22 | + |
| 23 | +```ini |
| 24 | +[authenticate] |
| 25 | +keyrings = .nvme |
| 26 | +``` |
| 27 | + |
| 28 | +No additional configuration is necessary for `tlshd`; simply start it as a daemon: |
| 29 | + |
| 30 | +```bash |
| 31 | +systemctl enable --now tlshd |
| 32 | +``` |
| 33 | + |
| 34 | +## Loading Keys on Boot or Module Load |
| 35 | + |
| 36 | +The NVMe subsystem loading keys from the kernel keystore, which means these keys must be available in the keystore before establishing a connection. |
| 37 | + |
| 38 | +### Creating a New Key |
| 39 | + |
| 40 | +```bash |
| 41 | +nvme gen-tls-key \ |
| 42 | + --hostnqn nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36 \ |
| 43 | + --subsysnqn nqn.io-1 --hmac 1 --identity 1 --insert --keyfile /etc/nvme/tls-keys |
| 44 | +``` |
| 45 | + |
| 46 | +This command creates a new host key, inserts it into the kernel keyring, and appends the derived TLS PSK to the keyfile (`/etc/nvme/tls-keys`). |
| 47 | + |
| 48 | +### Inserting an Existing Key |
| 49 | + |
| 50 | +```bash |
| 51 | +nvme check-tls-key \ |
| 52 | + --hostnqn nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36 \ |
| 53 | + --subsysnqn nqn.io-1 --identity 1 \ |
| 54 | + --keydata NVMeTLSkey-1:01:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtVQoZ: \ |
| 55 | + --insert --keyfile /etc/nvme/tls-keys |
| 56 | +``` |
| 57 | + |
| 58 | +This command inserts the configured key (`--keydata`) into the kernel keyring and appends the derived TLS PSK to the keyfile. |
| 59 | + |
| 60 | +### Loading Keys on Boot or Module Load |
| 61 | + |
| 62 | +The kernel keyring does not persist keys, so userland must import keys into the keyring upon each boot or module load (for NVMe-TCP). To load keys: |
| 63 | + |
| 64 | +```bash |
| 65 | +nvme tls --import /etc/nvme/tls-keys |
| 66 | +``` |
| 67 | + |
| 68 | +The `70-nvmf-keys.rules` udev rule ([source](https://github.com/linux-nvme/nvme-cli/blob/master/nvmf-autoconnect/udev-rules/70-nvmf-keys.rules.in)) will load keys from `/etc/nvme/tls-keys` automatically. |
| 69 | + |
| 70 | +### Recommendation for Handling TLS Keys |
| 71 | + |
| 72 | +The `nvme connect` command also allows passing a TLS key directly via the command line or a JSON config file. Avoid this method in production environments, as it may expose keys. |
| 73 | + |
| 74 | +## Establishing a Connection |
| 75 | + |
| 76 | +Once the keys are in the keystore, add the `--tls` option to establish a secure connection: |
| 77 | + |
| 78 | +```bash |
| 79 | +nvme connect --transport tcp --traddr 192.168.154.148 --trsvcid 4420 \ |
| 80 | + --hostnqn nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36 \ |
| 81 | + --hostid befdec4c-2234-11b2-a85c-ca77c773af36 \ |
| 82 | + --nqn nqn.io-1 --tls --dump-config --output-format json |
| 83 | +``` |
| 84 | + |
| 85 | +The resulting JSON output can be saved to simplify future connections: |
| 86 | + |
| 87 | +```json |
| 88 | +[ |
| 89 | + { |
| 90 | + "hostnqn": "nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36", |
| 91 | + "hostid": "befdec4c-2234-11b2-a85c-ca77c773af36", |
| 92 | + "subsystems": [ |
| 93 | + { |
| 94 | + "nqn": "nqn.io-1", |
| 95 | + "ports": [ |
| 96 | + { |
| 97 | + "transport": "tcp", |
| 98 | + "traddr": "192.168.154.148", |
| 99 | + "trsvcid": "4420", |
| 100 | + "dhchap_key": "none", |
| 101 | + "tls": true |
| 102 | + } |
| 103 | + ] |
| 104 | + } |
| 105 | + ] |
| 106 | + } |
| 107 | +] |
| 108 | +``` |
| 109 | + |
| 110 | +Using this JSON file, you can connect with: |
| 111 | + |
| 112 | +```bash |
| 113 | +nvme connect --config config.json |
| 114 | +``` |
| 115 | + |
| 116 | +## Setting Up the Target |
| 117 | + |
| 118 | +The same steps for creating keys and importing/exporting keys to/from the kernel are necessary for the target as they are for the host (see above). |
| 119 | + |
| 120 | +For the above example, you can use the `nvmetcli` config: |
| 121 | + |
| 122 | +```json |
| 123 | +{ |
| 124 | + "hosts": [ |
| 125 | + { |
| 126 | + "nqn": "nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36" |
| 127 | + } |
| 128 | + ], |
| 129 | + "ports": [ |
| 130 | + { |
| 131 | + "addr": { |
| 132 | + "adrfam": "ipv4", |
| 133 | + "traddr": "0.0.0.0", |
| 134 | + "treq": "not specified", |
| 135 | + "trsvcid": "4420", |
| 136 | + "trtype": "tcp", |
| 137 | + "tsas": "tls1.3" |
| 138 | + }, |
| 139 | + "ana_groups": [ |
| 140 | + { |
| 141 | + "ana": { |
| 142 | + "state": "optimized" |
| 143 | + }, |
| 144 | + "grpid": 1 |
| 145 | + } |
| 146 | + ], |
| 147 | + "param": { |
| 148 | + "inline_data_size": "16384", |
| 149 | + "pi_enable": "0" |
| 150 | + }, |
| 151 | + "portid": 0, |
| 152 | + "referrals": [], |
| 153 | + "subsystems": [ |
| 154 | + "nqn.io-1" |
| 155 | + ] |
| 156 | + } |
| 157 | + ], |
| 158 | + "subsystems": [ |
| 159 | + { |
| 160 | + "allowed_hosts": [ |
| 161 | + "nqn.2014-08.org.nvmexpress:uuid:befdec4c-2234-11b2-a85c-ca77c773af36" |
| 162 | + ], |
| 163 | + "attr": { |
| 164 | + "allow_any_host": "0", |
| 165 | + "cntlid_max": "65519", |
| 166 | + "cntlid_min": "1", |
| 167 | + "firmware": "6.8.0-rc", |
| 168 | + "ieee_oui": "0x000000", |
| 169 | + "model": "Linux", |
| 170 | + "pi_enable": "0", |
| 171 | + "qid_max": "128", |
| 172 | + "serial": "0c74361069d9db6c65ef", |
| 173 | + "version": "1.3" |
| 174 | + }, |
| 175 | + "namespaces": [ |
| 176 | + { |
| 177 | + "ana": { |
| 178 | + "grpid": "1" |
| 179 | + }, |
| 180 | + "ana_grpid": 1, |
| 181 | + "device": { |
| 182 | + "nguid": "00000000-0000-0000-0000-000000000000", |
| 183 | + "path": "/dev/vdb", |
| 184 | + "uuid": "91fdba0d-f87b-4c25-b80f-db7be1418b9e" |
| 185 | + }, |
| 186 | + "enable": 1, |
| 187 | + "nsid": 1 |
| 188 | + } |
| 189 | + ], |
| 190 | + "nqn": "nqn.io-1" |
| 191 | + } |
| 192 | + ] |
| 193 | +} |
| 194 | +``` |
0 commit comments