From bf655ec7a16b98d75c5090e5446818773dcf6c52 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Sat, 19 Sep 2026 21:42:40 -0700 Subject: [PATCH] fix(kms): deny on an empty device allowlist in auth-simple --- dstack/kms/auth-simple/README.md | 4 +-- dstack/kms/auth-simple/index.test.ts | 46 ++++++++++++++++++++++++++++ dstack/kms/auth-simple/index.ts | 4 +-- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/dstack/kms/auth-simple/README.md b/dstack/kms/auth-simple/README.md index 6857122d1..efd2efcb5 100644 --- a/dstack/kms/auth-simple/README.md +++ b/dstack/kms/auth-simple/README.md @@ -102,10 +102,10 @@ raw NitroTPM evidence by itself. | `allowedTcbStatuses` | No | Allowed verifier-derived TCB status strings. Defaults to `["UpToDate"]`; non-up-to-date SNP/TDX statuses remain fail-closed unless explicitly allowlisted for testing. | | `allowedAdvisoryIds` | No | Advisory IDs permitted in `advisoryIds`. Defaults to `[]`, which rejects any advisory. | | `kms.mrAggregated` | Yes | Allowed KMS early aggregate MR values (boot-mr-done). | -| `kms.devices` | No | Allowed KMS device IDs | +| `kms.devices` | No | Allowed KMS device IDs. Empty denies all unless `kms.allowAnyDevice` is true. | | `kms.allowAnyDevice` | No | If true, skip device ID check for KMS | | `apps..composeHashes` | No | Allowed compose hashes for this app | -| `apps..devices` | No | Allowed device IDs for this app | +| `apps..devices` | No | Allowed device IDs for this app. Empty denies all unless `allowAnyDevice` is true. | | `apps..allowAnyDevice` | No | If true, skip device ID check for this app | For experimental AMD SEV-SNP dry-run authorization, keep the default fail-closed TCB policy unless you intentionally want the auth webhook to accept non-up-to-date verifier-derived SNP `BootInfo`. To exercise the dry-run path without enabling key release, allowlist the recomputed SNP `mrAggregated`, `osImageHash`, app/compose identity, device/chip identity, and any non-default `allowedTcbStatuses`/`allowedAdvisoryIds` values explicitly. KMS still rejects SNP before returning app keys, KMS keys, or app certificates. diff --git a/dstack/kms/auth-simple/index.test.ts b/dstack/kms/auth-simple/index.test.ts index 32ad06772..8d1ca4309 100644 --- a/dstack/kms/auth-simple/index.test.ts +++ b/dstack/kms/auth-simple/index.test.ts @@ -240,6 +240,28 @@ describe('auth-simple', () => { expect(json.isAllowed).toBe(false); expect(json.reason).toContain('MR'); }); + it('rejects KMS boot with an empty device allowlist', async () => { + writeTestConfig({ + gatewayAppId: '0xgateway', + osImages: ['0x1fbb0cf9cc6cfbf23d6b779776fabad2c5403d643badb9e5e238615e4960a78a'], + kms: { + mrAggregated: ['0xabc123'], + devices: [], + allowAnyDevice: false + } + }); + + const res = await app.fetch(new Request('http://localhost/bootAuth/kms', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(baseBootInfo) + })); + const json = await res.json(); + + expect(json.isAllowed).toBe(false); + expect(json.reason).toContain('device'); + }); + it('allows KMS boot with allowAnyDevice', async () => { writeTestConfig({ gatewayAppId: '0xgateway', @@ -352,6 +374,30 @@ describe('auth-simple', () => { expect(json.reason).toContain('device'); }); + it('rejects app boot with an empty device allowlist', async () => { + writeTestConfig({ + gatewayAppId: '0xgateway', + osImages: ['0x1fbb0cf9cc6cfbf23d6b779776fabad2c5403d643badb9e5e238615e4960a78a'], + apps: { + '0xapp123': { + composeHashes: ['0xcompose456'], + devices: [], + allowAnyDevice: false + } + } + }); + + const res = await app.fetch(new Request('http://localhost/bootAuth/app', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(baseBootInfo) + })); + const json = await res.json(); + + expect(json.isAllowed).toBe(false); + expect(json.reason).toContain('device'); + }); + it('allows app boot with allowAnyDevice', async () => { writeTestConfig({ gatewayAppId: '0xgateway', diff --git a/dstack/kms/auth-simple/index.ts b/dstack/kms/auth-simple/index.ts index 4a833d1ec..7f9a7f036 100644 --- a/dstack/kms/auth-simple/index.ts +++ b/dstack/kms/auth-simple/index.ts @@ -149,7 +149,7 @@ class ConfigBackend { // check device ID if (!config.kms.allowAnyDevice) { const allowedDevices = config.kms.devices.map(normalizeHex); - if (allowedDevices.length > 0 && !allowedDevices.includes(deviceId)) { + if (!allowedDevices.includes(deviceId)) { return { isAllowed: false, reason: 'KMS is not allowed to boot on this device', @@ -195,7 +195,7 @@ class ConfigBackend { // check device ID if (!appConfig.allowAnyDevice) { const allowedDevices = appConfig.devices.map(normalizeHex); - if (allowedDevices.length > 0 && !allowedDevices.includes(deviceId)) { + if (!allowedDevices.includes(deviceId)) { return { isAllowed: false, reason: 'app is not allowed to boot on this device',