Skip to content

Commit 630a8fe

Browse files
committed
feat(auth-mock): add configurable authorization policies
Support MOCK_POLICY env var to control boot authorization behavior: - allow-all (default): all requests allowed - deny-kms: reject KMS self-authorization - deny-app: reject app authorization - deny-all: reject all requests - allowlist-device: only allow specified MOCK_ALLOWED_DEVICE_IDS - allowlist-mr: only allow specified MOCK_ALLOWED_MR_AGGREGATED
1 parent 69d3f0b commit 630a8fe

3 files changed

Lines changed: 78 additions & 9 deletions

File tree

kms/auth-mock/Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM oven/bun:1-alpine
2+
WORKDIR /app
3+
4+
ARG DSTACK_REV
5+
6+
RUN apk add --no-cache git
7+
RUN git clone https://github.com/Dstack-TEE/dstack.git && \
8+
cd dstack && \
9+
git fetch origin ${DSTACK_REV} && \
10+
git checkout ${DSTACK_REV}
11+
WORKDIR /app/dstack/kms/auth-mock
12+
RUN bun install --frozen-lockfile
13+
CMD ["bun", "index.ts"]

kms/auth-mock/bun.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kms/auth-mock/index.ts

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,29 @@ const BootResponseSchema = z.object({
3030
type BootInfo = z.infer<typeof BootInfoSchema>;
3131
type BootResponse = z.infer<typeof BootResponseSchema>;
3232

33+
// authorization policy - configurable via environment variables
34+
// MOCK_POLICY: "allow-all" (default), "deny-kms", "deny-app", "deny-all",
35+
// "allowlist-device", "allowlist-mr"
36+
// MOCK_ALLOWED_DEVICE_IDS: comma-separated device IDs (for allowlist-device policy)
37+
// MOCK_ALLOWED_MR_AGGREGATED: comma-separated MR aggregated values (for allowlist-mr policy)
38+
39+
type MockPolicy = 'allow-all' | 'deny-kms' | 'deny-app' | 'deny-all' | 'allowlist-device' | 'allowlist-mr';
40+
41+
function getPolicy(): MockPolicy {
42+
const policy = process.env.MOCK_POLICY || 'allow-all';
43+
const valid: MockPolicy[] = ['allow-all', 'deny-kms', 'deny-app', 'deny-all', 'allowlist-device', 'allowlist-mr'];
44+
if (!valid.includes(policy as MockPolicy)) {
45+
console.warn(`unknown MOCK_POLICY "${policy}", falling back to allow-all`);
46+
return 'allow-all';
47+
}
48+
return policy as MockPolicy;
49+
}
50+
51+
function parseList(envVar: string): Set<string> {
52+
const raw = process.env[envVar] || '';
53+
return new Set(raw.split(',').map(s => s.trim().toLowerCase()).filter(Boolean));
54+
}
55+
3356
// mock backend class - no blockchain interaction
3457
class MockBackend {
3558
private mockGatewayAppId: string;
@@ -44,14 +67,45 @@ class MockBackend {
4467
}
4568

4669
async checkBoot(bootInfo: BootInfo, isKms: boolean): Promise<BootResponse> {
47-
// always return success for mock backend
48-
const reason = isKms ? 'mock KMS always allowed' : 'mock app always allowed';
49-
50-
return {
70+
const policy = getPolicy();
71+
const deny = (reason: string): BootResponse => ({
72+
isAllowed: false,
73+
reason,
74+
gatewayAppId: '',
75+
});
76+
const allow = (reason: string): BootResponse => ({
5177
isAllowed: true,
5278
reason,
5379
gatewayAppId: this.mockGatewayAppId,
54-
};
80+
});
81+
82+
switch (policy) {
83+
case 'deny-all':
84+
return deny(`mock policy: deny-all`);
85+
case 'deny-kms':
86+
if (isKms) return deny(`mock policy: deny-kms`);
87+
return allow('mock app allowed (deny-kms policy)');
88+
case 'deny-app':
89+
if (!isKms) return deny(`mock policy: deny-app`);
90+
return allow('mock KMS allowed (deny-app policy)');
91+
case 'allowlist-device': {
92+
const allowed = parseList('MOCK_ALLOWED_DEVICE_IDS');
93+
const deviceId = bootInfo.deviceId.toLowerCase().replace(/^0x/, '');
94+
if (allowed.size === 0) return deny('mock policy: allowlist-device with empty list');
95+
if (!allowed.has(deviceId)) return deny(`mock policy: device ${bootInfo.deviceId} not in allowlist`);
96+
return allow(`mock policy: device ${bootInfo.deviceId} allowed`);
97+
}
98+
case 'allowlist-mr': {
99+
const allowed = parseList('MOCK_ALLOWED_MR_AGGREGATED');
100+
const mr = bootInfo.mrAggregated.toLowerCase().replace(/^0x/, '');
101+
if (allowed.size === 0) return deny('mock policy: allowlist-mr with empty list');
102+
if (!allowed.has(mr)) return deny(`mock policy: mrAggregated ${bootInfo.mrAggregated} not in allowlist`);
103+
return allow(`mock policy: mrAggregated ${bootInfo.mrAggregated} allowed`);
104+
}
105+
case 'allow-all':
106+
default:
107+
return allow(isKms ? 'mock KMS always allowed' : 'mock app always allowed');
108+
}
55109
}
56110

57111
async getGatewayAppId(): Promise<string> {
@@ -85,6 +139,7 @@ app.get('/', async (c) => {
85139
return c.json({
86140
status: 'ok',
87141
kmsContractAddr: process.env.KMS_CONTRACT_ADDR || '0xmockcontract1234567890123456789012345678',
142+
ethRpcUrl: process.env.ETH_RPC_URL || '',
88143
gatewayAppId: batch[0],
89144
chainId: batch[1],
90145
appAuthImplementation: batch[2], // NOTE: for backward compatibility
@@ -155,8 +210,8 @@ app.post('/bootAuth/kms',
155210

156211
// start server
157212
const port = parseInt(process.env.PORT || '3000');
158-
console.log(`starting mock auth server on port ${port}`);
159-
console.log('note: this is a mock backend - all authentications will succeed');
213+
const policy = getPolicy();
214+
console.log(`starting mock auth server on port ${port} (policy: ${policy})`);
160215

161216
export default {
162217
port,

0 commit comments

Comments
 (0)