CVE-2025-59054
Description
dstack is a software development kit (SDK) to simplify the deployment of arbitrary containerized apps into trusted execution environments. In versions of dstack prior to 0.5.4, a malicious host may provide a crafted LUKS2 data volume to a dstack CVM for use as the /data mount. The guest will open the volume and write secret data using a volume key known to the attacker, causing disclosure of Wireguard keys and other secret information. The attacker can also pre-load data on the device, which could potentially compromise guest execution. LUKS2 volume metadata is not authenticated and supports null key-encryption algorithms, allowing an attacker to create a volume such that the volume opens (cryptsetup open) without error using any passphrase or token, records all writes in plaintext (or ciphertext with an attacker-known key), and/or contains arbitrary data chosen by the attacker. Version 0.5.4 of dstack contains a patch that addresses LUKS headers.
Affected products
1- Range: gateway-v0.5.3, kms-v0.5.3, kms-v0.5.4, …
Patches
2fb75a412cb72Merge pull request #342 from Dstack-TEE/docker-org
4 files changed · +10 −10
.github/workflows/gateway-release.yml+3 −3 modified@@ -51,7 +51,7 @@ jobs: with: context: gateway/dstack-app/builder push: true - tags: ${{ vars.DOCKERHUB_USERNAME }}/gateway:${{ env.VERSION }} + tags: ${{ vars.DOCKERHUB_ORG }}/dstack-gateway:${{ env.VERSION }} platforms: linux/amd64 provenance: false build-args: | @@ -61,7 +61,7 @@ jobs: - name: Generate artifact attestation uses: actions/attest-build-provenance@v1 with: - subject-name: "docker.io/${{ vars.DOCKERHUB_USERNAME }}/gateway" + subject-name: "docker.io/${{ vars.DOCKERHUB_ORG }}/dstack-gateway" subject-digest: ${{ steps.build-and-push.outputs.digest }} push-to-registry: true @@ -72,7 +72,7 @@ jobs: body: | ## Docker Image Information - **Image**: `docker.io/${{ vars.DOCKERHUB_USERNAME }}/gateway:${{ env.VERSION }}` + **Image**: `docker.io/${{ vars.DOCKERHUB_ORG }}/dstack-gateway:${{ env.VERSION }}` **Digest (SHA256)**: `${{ steps.build-and-push.outputs.digest }}`
.github/workflows/kms-release.yml+3 −3 modified@@ -54,7 +54,7 @@ jobs: with: context: kms/dstack-app/builder push: true - tags: ${{ vars.DOCKERHUB_USERNAME }}/kms:${{ env.VERSION }} + tags: ${{ vars.DOCKERHUB_ORG }}/dstack-kms:${{ env.VERSION }} platforms: linux/amd64 provenance: false build-args: | @@ -65,7 +65,7 @@ jobs: - name: Generate artifact attestation uses: actions/attest-build-provenance@v1 with: - subject-name: "docker.io/${{ vars.DOCKERHUB_USERNAME }}/kms" + subject-name: "docker.io/${{ vars.DOCKERHUB_ORG }}/dstack-kms" subject-digest: ${{ steps.build-and-push.outputs.digest }} push-to-registry: true @@ -92,7 +92,7 @@ jobs: body: | ## Docker Image Information - **Image**: `docker.io/${{ vars.DOCKERHUB_USERNAME }}/kms:${{ env.VERSION }}` + **Image**: `docker.io/${{ vars.DOCKERHUB_ORG }}/dstack-kms:${{ env.VERSION }}` **Digest (SHA256)**: `${{ steps.build-and-push.outputs.digest }}`
.github/workflows/verifier-release.yml+3 −3 modified@@ -51,7 +51,7 @@ jobs: context: verifier file: verifier/builder/Dockerfile push: true - tags: ${{ vars.DOCKERHUB_USERNAME }}/dstack-verifier:${{ env.VERSION }} + tags: ${{ vars.DOCKERHUB_ORG }}/dstack-verifier:${{ env.VERSION }} platforms: linux/amd64 provenance: false build-args: | @@ -62,7 +62,7 @@ jobs: - name: Generate artifact attestation uses: actions/attest-build-provenance@v1 with: - subject-name: "docker.io/${{ vars.DOCKERHUB_USERNAME }}/dstack-verifier" + subject-name: "docker.io/${{ vars.DOCKERHUB_ORG }}/dstack-verifier" subject-digest: ${{ steps.build-and-push.outputs.digest }} push-to-registry: true @@ -73,7 +73,7 @@ jobs: body: | ## Docker Image Information - **Image**: `docker.io/${{ vars.DOCKERHUB_USERNAME }}/dstack-verifier:${{ env.VERSION }}` + **Image**: `docker.io/${{ vars.DOCKERHUB_ORG }}/dstack-verifier:${{ env.VERSION }}` **Digest (SHA256)**: `${{ steps.build-and-push.outputs.digest }}`
verifier/README.md+1 −1 modified@@ -102,7 +102,7 @@ DSTACK_VERIFIER_PORT=8080 cargo run --bin dstack-verifier ```yaml services: dstack-verifier: - image: kvin/dstack-verifier:latest + image: dstacktee/dstack-verifier:latest ports: - "8080:8080" restart: unless-stopped
e36ad5f732d8Merge branch 'advisory-fix-1'
6 files changed · +622 −61
Cargo.lock+337 −53 modified@@ -27,14 +27,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug", +] + [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -45,8 +57,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", - "aes", - "cipher", + "aes 0.8.4", + "cipher 0.4.4", "ctr", "ghash", "subtle", @@ -190,7 +202,7 @@ dependencies = [ "derive_more 2.0.1", "either", "serde", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -292,7 +304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" dependencies = [ "alloy-rlp-derive", - "arrayvec", + "arrayvec 0.7.6", "bytes", ] @@ -458,7 +470,7 @@ checksum = "983d99aa81f586cef9dae38443245e585840fcf0fc58b09aee0b1f27aed1d500" dependencies = [ "alloy-primitives", "alloy-rlp", - "arrayvec", + "arrayvec 0.7.6", "derive_more 2.0.1", "nybbles", "serde", @@ -673,6 +685,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.6" @@ -882,6 +900,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.22.1" @@ -909,6 +933,15 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.69.5" @@ -1016,6 +1049,26 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -1043,7 +1096,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30" dependencies = [ - "base64", + "base64 0.22.1", "bollard-stubs", "bytes", "futures-core", @@ -1211,7 +1264,7 @@ dependencies = [ "serde", "serde-human-bytes", "serde_json", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -1302,6 +1355,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1456,6 +1518,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "convert_case" version = "0.6.0" @@ -1574,6 +1642,31 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi", + "lazy_static", + "libc", + "mio 0.7.14", + "parking_lot 0.11.2", + "signal-hook 0.1.17", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.4" @@ -1603,6 +1696,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "ct_monitor" version = "0.5.3" @@ -1628,7 +1731,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -1750,7 +1853,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core", + "parking_lot_core 0.9.11", ] [[package]] @@ -1767,7 +1870,7 @@ checksum = "32ef42eade99e79b9fb9d31532041a0a380a7e849d3486950a40b1afd5bf417c" dependencies = [ "anyhow", "asn1_der", - "base64", + "base64 0.22.1", "byteorder", "chrono", "const-oid", @@ -1969,7 +2072,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", @@ -2083,7 +2186,7 @@ dependencies = [ "serde", "serde-duration", "serde_json", - "sha2", + "sha2 0.10.9", "shared_child", "smallvec", "tokio", @@ -2110,7 +2213,7 @@ name = "dstack-guest-agent" version = "0.5.3" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "bollard", "cert-client", "chrono", @@ -2138,7 +2241,7 @@ dependencies = [ "sd-notify", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "sha3", "strip-ansi-escapes", "sysinfo", @@ -2191,7 +2294,7 @@ dependencies = [ "serde-duration", "serde-human-bytes", "serde_json", - "sha2", + "sha2 0.10.9", "sha3", "tempfile", "tokio", @@ -2230,7 +2333,7 @@ dependencies = [ "serde", "serde-human-bytes", "serde_json", - "sha2", + "sha2 0.10.9", "thiserror 2.0.15", ] @@ -2263,7 +2366,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tokio", "x509-parser", ] @@ -2280,7 +2383,7 @@ dependencies = [ "pkcs8", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tokio", ] @@ -2315,17 +2418,19 @@ dependencies = [ "host-api", "k256", "libc", + "luks2", "parity-scale-codec", "ra-rpc", "ra-tls", "rand 0.8.5", "regex", "schnorrkel", + "scopeguard", "serde", "serde-human-bytes", "serde_json", "serde_yaml2", - "sha2", + "sha2 0.10.9", "sha3", "sodiumbox", "tdx-attest", @@ -2342,7 +2447,7 @@ name = "dstack-vmm" version = "0.5.3" dependencies = [ "anyhow", - "base64", + "base64 0.22.1", "bon", "clap", "dirs", @@ -2369,7 +2474,7 @@ dependencies = [ "serde", "serde-human-bytes", "serde_json", - "sha2", + "sha2 0.10.9", "shared_child", "strip-ansi-escapes", "supervisor-client", @@ -2548,7 +2653,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "auto_impl", "bytes", ] @@ -2559,7 +2664,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "auto_impl", "bytes", ] @@ -3045,7 +3150,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", ] [[package]] @@ -3127,7 +3232,7 @@ dependencies = [ "ipconfig", "lru-cache", "once_cell", - "parking_lot", + "parking_lot 0.12.4", "rand 0.8.5", "resolv-conf", "smallvec", @@ -3148,7 +3253,7 @@ dependencies = [ "ipconfig", "moka", "once_cell", - "parking_lot", + "parking_lot 0.12.4", "rand 0.9.2", "resolv-conf", "smallvec", @@ -3163,7 +3268,17 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", ] [[package]] @@ -3353,7 +3468,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -3611,14 +3726,23 @@ dependencies = [ "similar", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "instant-acme" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37221e690dcc5d0ea7c1f70decda6ae3495e72e8af06bca15e982193ffdf4fc4" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", "http", "http-body", @@ -3662,7 +3786,7 @@ dependencies = [ "clap", "fs-err", "hex_fmt", - "sha2", + "sha2 0.10.9", "sha3", ] @@ -3807,7 +3931,7 @@ dependencies = [ "elliptic-curve", "once_cell", "serdect", - "sha2", + "sha2 0.10.9", "signature", ] @@ -4005,6 +4129,28 @@ dependencies = [ "insta", ] +[[package]] +name = "luks2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2044d8bd5489b199890c3dbf38d4c8f50f3a5a38833986808b14e2367fe267fa" +dependencies = [ + "aes 0.7.5", + "base64 0.13.1", + "bincode", + "crossterm", + "hmac 0.11.0", + "pbkdf2", + "rust-argon2", + "secrecy", + "serde", + "serde-big-array", + "serde_json", + "sha2 0.9.9", + "thiserror 1.0.69", + "xts-mode", +] + [[package]] name = "macro-string" version = "0.1.4" @@ -4095,6 +4241,19 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi 0.3.7", + "winapi", +] + [[package]] name = "mio" version = "1.0.4" @@ -4107,6 +4266,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + [[package]] name = "moka" version = "0.12.10" @@ -4117,7 +4285,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "loom 0.7.2", - "parking_lot", + "parking_lot 0.12.4", "portable-atomic", "rustc_version 0.4.1", "smallvec", @@ -4251,7 +4419,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio", + "mio 1.0.4", "notify-types", "walkdir", "windows-sys 0.60.2", @@ -4263,6 +4431,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -4481,7 +4658,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -4512,7 +4689,7 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "bitvec", "byte-slice-cast", "const_format", @@ -4534,14 +4711,39 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.11", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -4552,7 +4754,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.17", "smallvec", "windows-targets 0.52.6", ] @@ -4581,6 +4783,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac", +] + [[package]] name = "pear" version = "0.2.9" @@ -4610,7 +4821,7 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ - "base64", + "base64 0.22.1", "serde", ] @@ -5193,7 +5404,7 @@ dependencies = [ "serde", "serde-human-bytes", "serde_json", - "sha2", + "sha2 0.10.9", "sha3", "tdx-attest", "tracing", @@ -5333,6 +5544,15 @@ dependencies = [ "yasna", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.17" @@ -5429,7 +5649,7 @@ version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures-channel", @@ -5479,7 +5699,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] @@ -5569,7 +5789,7 @@ dependencies = [ "memchr", "multer", "num_cpus", - "parking_lot", + "parking_lot 0.12.4", "pin-project-lite", "rand 0.8.5", "ref-cast", @@ -5689,6 +5909,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64 0.13.1", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -6014,7 +6246,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -6092,13 +6324,13 @@ checksum = "6e9fcb6c2e176e86ec703e22560d99d65a5ee9056ae45a08e13e84ebf796296f" dependencies = [ "aead", "arrayref", - "arrayvec", + "arrayvec 0.7.6", "curve25519-dalek", "getrandom_or_panic", "merlin", "rand_core 0.6.4", "serde_bytes", - "sha2", + "sha2 0.10.9", "subtle", "zeroize", ] @@ -6160,6 +6392,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "security-framework" version = "3.3.0" @@ -6216,6 +6457,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd31f59f6fe2b0c055371bb2f16d7f0aa7d8881676c04a55b1596d1a17cd10a4" +dependencies = [ + "serde", +] + [[package]] name = "serde-duration" version = "0.5.3" @@ -6315,7 +6565,7 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ - "base64", + "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", @@ -6362,6 +6612,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.9" @@ -6427,7 +6690,18 @@ checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" dependencies = [ "libc", "os_pipe", - "signal-hook", + "signal-hook 0.3.18", +] + +[[package]] +name = "signal-hook" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +dependencies = [ + "libc", + "mio 0.7.14", + "signal-hook-registry", ] [[package]] @@ -6711,7 +6985,7 @@ checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" dependencies = [ "libc", "memchr", - "ntapi", + "ntapi 0.4.1", "objc2-core-foundation", "objc2-io-kit", "windows 0.61.3", @@ -6774,7 +7048,7 @@ dependencies = [ "serde", "serde-human-bytes", "serde_json", - "sha2", + "sha2 0.10.9", "tdx-attest-sys", "thiserror 2.0.15", ] @@ -6964,8 +7238,8 @@ dependencies = [ "bytes", "io-uring", "libc", - "mio", - "parking_lot", + "mio 1.0.4", + "parking_lot 0.12.4", "pin-project-lite", "signal-hook-registry", "slab", @@ -7170,7 +7444,7 @@ dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "parking_lot", + "parking_lot 0.12.4", "regex", "sharded-slab", "smallvec", @@ -8017,6 +8291,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "xts-mode" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a099a2f21d48275314733f85bc43b6c6213b66394233aaea573fc7a520dcd9" +dependencies = [ + "byteorder", + "cipher 0.3.0", +] + [[package]] name = "yaml-rust2" version = "0.8.1"
Cargo.toml+3 −0 modified@@ -211,3 +211,6 @@ which = "7.0.2" smallvec = "1.14.0" cmd_lib = "1.9.5" serde_yaml2 = "0.1.2" + +luks2 = "0.5.0" +scopeguard = "1.2.0"
dstack-util/Cargo.toml+2 −0 modified@@ -49,6 +49,8 @@ serde_yaml2.workspace = true bollard.workspace = true sodiumbox.workspace = true libc.workspace = true +luks2.workspace = true +scopeguard.workspace = true [dev-dependencies] rand.workspace = true
dstack-util/src/system_setup.rs+280 −8 modified@@ -18,9 +18,14 @@ use dstack_types::{ KeyProvider, KeyProviderInfo, }; use fs_err as fs; +use luks2::{ + LuksAf, LuksConfig, LuksDigest, LuksHeader, LuksJson, LuksKdf, LuksKeyslot, LuksSegment, + LuksSegmentSize, +}; use ra_rpc::client::{CertInfo, RaClient, RaClientConfig}; use ra_tls::cert::generate_ra_cert; use rand::Rng as _; +use scopeguard::defer; use serde::{Deserialize, Serialize}; use tdx_attest::extend_rtmr3; use tracing::{info, warn}; @@ -449,10 +454,7 @@ impl<'a> Stage0<'a> { .notify_q("boot.progress", "mounting data disk") .await; info!("Mounting encrypted data disk"); - let root_hd = &self.args.device; - let disk_crypt_key = disk_crypt_key.trim(); - cmd!(echo -n $disk_crypt_key | cryptsetup luksOpen --type luks2 -d- $root_hd $name) - .or(Err(anyhow!("Failed to open encrypted data disk")))?; + self.open_encrypted_volume(disk_crypt_key, name)?; cmd! { zpool import dstack; zpool status dstack; @@ -468,15 +470,55 @@ impl<'a> Stage0<'a> { fn luks_setup(&self, disk_crypt_key: &str, name: &str) -> Result<()> { let root_hd = &self.args.device; + let sector_offset = PAYLOAD_OFFSET / 512; cmd! { info "Formatting encrypted disk"; echo -n $disk_crypt_key | - cryptsetup luksFormat --type luks2 --cipher aes-xts-plain64 --pbkdf pbkdf2 -d- $root_hd $name; + cryptsetup luksFormat + --type luks2 + --offset $sector_offset + --cipher aes-xts-plain64 + --pbkdf pbkdf2 + -d- + $root_hd + $name; + } + .or(Err(anyhow!("Failed to setup luks volume")))?; + self.open_encrypted_volume(disk_crypt_key, name) + } + + fn open_encrypted_volume(&self, disk_crypt_key: &str, name: &str) -> Result<()> { + let root_hd = &self.args.device; + let disk_crypt_key = disk_crypt_key.trim(); + // Create a private tmpfs mount to ensure the header stays in-memory. + let tmp_hdr_dir = "/tmp/dstack-luks-header"; + let in_mem_hdr = format!("{tmp_hdr_dir}/luks-header"); + defer! { + // Ensure cleanup of header file and tmpfs mount. + cmd! { + info "Cleaning up in-memory LUKS header"; + rm -f $in_mem_hdr; + umount $tmp_hdr_dir; + rmdir $tmp_hdr_dir; + }.ok(); + } + cmd! { + info "Mounting tmpfs for in-memory LUKS header"; + mkdir -p $tmp_hdr_dir; + mount -t tmpfs -o size=64M,mode=0700,nosuid,nodev,noexec tmpfs $tmp_hdr_dir; + info "Loading the LUKS2 header"; + cryptsetup luksHeaderBackup --header-backup-file=$in_mem_hdr $root_hd; + } + .context("Failed to load LUKS2 header")?; + let hdr_file = fs::File::open(&in_mem_hdr).context("Failed to open LUKS2 header")?; + validate_luks2_headers(hdr_file).context("Failed to validate LUKS2 header")?; + + cmd! { info "Opening the device"; - echo -n $disk_crypt_key | - cryptsetup luksOpen --type luks2 -d- $root_hd $name; - }.or(Err(anyhow!("Failed to setup luks volume")))?; + echo -n $disk_crypt_key | cryptsetup luksOpen --type luks2 --header $in_mem_hdr -d- $root_hd $name; + } + .or(Err(anyhow!("Failed to open encrypted data disk")))?; Ok(()) } @@ -914,3 +956,233 @@ impl Stage1<'_> { Ok(()) } } + +macro_rules! const_pad { + ($s:expr, $len:expr) => { + const { + assert!($s.len() <= $len, "The s is too long"); + let mut padded: [u8; $len] = [0; $len]; + let mut i = 0; + while i < $s.len() { + padded[i] = $s[i]; + i += 1; + } + padded + } + }; +} + +const PAYLOAD_OFFSET: u64 = 16777216; + +fn validate_luks2_headers(mut reader: impl std::io::Read) -> Result<()> { + validate_single_luks2_header(&mut reader, 0)?; + validate_single_luks2_header(&mut reader, 1)?; + Ok(()) +} + +fn validate_single_luks2_header(mut reader: impl std::io::Read, hdr_ind: u64) -> Result<()> { + let mut hdr_data = vec![0u8; 4096]; + reader + .read_exact(&mut hdr_data) + .context("Failed to read LUKS header")?; + let header = + LuksHeader::read_from(&mut &hdr_data[..]).context("Failed to decode LUKS header")?; + let LuksHeader { + magic, + version, + hdr_size, + seqid: _, + label, + csum_alg, + salt: _, + uuid: _, + subsystem, + hdr_offset, + csum: _, + .. + } = header; + + let expected_magic = match hdr_ind { + 0 => [76, 85, 75, 83, 186, 190], + 1 => [83, 75, 85, 76, 186, 190], + _ => bail!("Invalid LUKS header index: {hdr_ind}"), + }; + if magic != expected_magic { + bail!("Invalid LUKS magic: {magic:?}"); + } + if version != 2 { + bail!("Invalid LUKS version: {version}"); + } + if label != [0; 48] { + bail!("Invalid LUKS label: {:?}", label); + } + if csum_alg != const_pad!(b"sha256", 32) { + bail!("Invalid LUKS checksum algorithm"); + } + if subsystem != [0; 48] { + bail!("Invalid LUKS subsystem"); + } + if hdr_offset != hdr_ind * hdr_size { + bail!("Invalid LUKS header offset: {hdr_offset}"); + } + if !(4096..=1024 * 1024 * 16).contains(&hdr_size) { + bail!("Invalid LUKS header size: {hdr_size}"); + } + + // Check JSON + let json_size = hdr_size - 4096; + let mut jsn_data = vec![0u8; json_size as usize]; + reader + .read_exact(&mut jsn_data) + .context("Failed to read LUKS JSON")?; + let json_end = jsn_data + .iter() + .position(|&b| b == 0) + .unwrap_or(jsn_data.len()); + jsn_data.truncate(json_end); + + let json = LuksJson::read_from(&mut &jsn_data[..]).context("Failed to decode LUKS JSON")?; + let LuksJson { + keyslots, + tokens, + segments, + digests, + config: + LuksConfig { + json_size: _, + keyslots_size: _, + flags, + requirements, + }, + } = json; + + if keyslots.len() != 1 { + bail!("Invalid LUKS keyslots"); + } + if !tokens.is_empty() { + bail!("Invalid LUKS tokens"); + } + if segments.len() != 1 { + bail!("Invalid LUKS segments"); + } + if digests.len() != 1 { + bail!("Invalid LUKS digests"); + } + if flags.is_some() { + bail!("Invalid LUKS flags"); + } + if requirements.is_some() { + bail!("Invalid LUKS requirements"); + } + + { + let first_keyslot = keyslots.get(&0).context("no LUKS keyslot")?; + let LuksKeyslot::luks2 { + key_size, + area, + kdf, + af, + priority, + } = first_keyslot; + if area.encryption() != "aes-xts-plain64" { + bail!("Invalid LUKS keyslot encryption: {}", area.encryption()); + } + if *key_size != 64 { + bail!("Invalid LUKS keyslot key size: {key_size}"); + } + if area.key_size() != 64 { + bail!("Invalid LUKS keyslot key size: {}", area.key_size()); + } + { + let LuksKdf::pbkdf2 { + hash, + iterations: _, + salt: _, + } = kdf + else { + bail!("Invalid LUKS keyslot KDF"); + }; + if hash != "sha256" { + bail!("Invalid LUKS keyslot hash: {hash}"); + } + } + { + let LuksAf::luks1 { hash, stripes } = af; + if hash != "sha256" { + bail!("Invalid LUKS keyslot hash: {hash}"); + } + if *stripes != 4000 { + bail!("Invalid LUKS keyslot stripes: {stripes}"); + } + } + if priority.is_some() { + bail!("Invalid LUKS keyslot priority"); + } + } + + { + let first_segment = segments.get(&0).context("no LUKS segment")?; + let LuksSegment::crypt { + offset, + size, + iv_tweak, + encryption, + sector_size, + integrity, + flags, + } = first_segment; + if *offset != PAYLOAD_OFFSET { + bail!("Invalid LUKS segment offset"); + } + if *size != LuksSegmentSize::dynamic { + bail!("Invalid LUKS segment size"); + } + if *iv_tweak != 0 { + bail!("Invalid LUKS segment IV tweak"); + } + if encryption != "aes-xts-plain64" { + bail!("Invalid LUKS segment encryption"); + } + if *sector_size != 512 { + bail!("Invalid LUKS segment sector size"); + } + if integrity.is_some() { + bail!("Invalid LUKS segment integrity"); + } + if flags.is_some() { + bail!("Invalid LUKS segment flags"); + } + } + { + let first_digest = digests.get(&0).context("no LUKS digest")?; + let LuksDigest::pbkdf2 { + keyslots, + segments, + hash, + digest: _, + iterations: _, + salt: _, + } = first_digest; + if hash != "sha256" { + bail!("Invalid LUKS digest hash: {hash}"); + } + if keyslots != &[0] { + bail!("Invalid LUKS digest keyslots: {keyslots:?}"); + } + if segments != &[0] { + bail!("Invalid LUKS digest segments: {segments:?}"); + } + } + Ok(()) +} + +#[test] +fn test_validate_luks2_header() { + let header_data = include_bytes!("../tests/fixtures/luks_header_good").to_vec(); + validate_luks2_headers(&mut &header_data[..]).expect("Failed to validate LUKS2 header"); + let header_data = include_bytes!("../tests/fixtures/luks_header_cipher_null").to_vec(); + let error = validate_luks2_headers(&mut &header_data[..]).unwrap_err(); + assert!(error + .to_string() + .contains("Invalid LUKS keyslot encryption")); +}
dstack-util/tests/fixtures/luks_header_cipher_null+0 −0 addeddstack-util/tests/fixtures/luks_header_good+0 −0 added
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- blog.trailofbits.com/2025/10/30/vulnerabilities-in-luks2-disk-encryption-for-confidential-vms/nvd
- github.com/Dstack-TEE/dstack/blob/04de4e422bb06f075b4215b2cfc410f5d7ac7aed/dstack-util/src/system_setup.rsnvd
- github.com/Dstack-TEE/dstack/commit/e36ad5f732d8821876a861934e1f47cda7b1a130nvd
- github.com/Dstack-TEE/dstack/security/advisories/GHSA-jxq2-hpw3-m5wfnvd
News mentions
0No linked articles in our index yet.