@de-otio/chaoskb-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/admin-handler/index.d.ts +22 -0
- package/dist/lib/admin-handler/index.d.ts.map +1 -0
- package/dist/lib/admin-handler/index.js +92 -0
- package/dist/lib/admin-handler/index.js.map +1 -0
- package/dist/lib/admin-handler/index.ts +123 -0
- package/dist/lib/admin-handler/routes/metrics.d.ts +11 -0
- package/dist/lib/admin-handler/routes/metrics.d.ts.map +1 -0
- package/dist/lib/admin-handler/routes/metrics.js +200 -0
- package/dist/lib/admin-handler/routes/metrics.js.map +1 -0
- package/dist/lib/admin-handler/routes/metrics.ts +234 -0
- package/dist/lib/admin-handler/routes/overview.d.ts +9 -0
- package/dist/lib/admin-handler/routes/overview.d.ts.map +1 -0
- package/dist/lib/admin-handler/routes/overview.js +110 -0
- package/dist/lib/admin-handler/routes/overview.js.map +1 -0
- package/dist/lib/admin-handler/routes/overview.ts +133 -0
- package/dist/lib/admin-handler/routes/tenants.d.ts +10 -0
- package/dist/lib/admin-handler/routes/tenants.d.ts.map +1 -0
- package/dist/lib/admin-handler/routes/tenants.js +108 -0
- package/dist/lib/admin-handler/routes/tenants.js.map +1 -0
- package/dist/lib/admin-handler/routes/tenants.ts +134 -0
- package/dist/lib/chaoskb-stack.d.ts +22 -0
- package/dist/lib/chaoskb-stack.d.ts.map +1 -0
- package/dist/lib/chaoskb-stack.js +60 -0
- package/dist/lib/chaoskb-stack.js.map +1 -0
- package/dist/lib/constructs/admin-api.d.ts +16 -0
- package/dist/lib/constructs/admin-api.d.ts.map +1 -0
- package/dist/lib/constructs/admin-api.js +93 -0
- package/dist/lib/constructs/admin-api.js.map +1 -0
- package/dist/lib/constructs/admin-dashboard.d.ts +18 -0
- package/dist/lib/constructs/admin-dashboard.d.ts.map +1 -0
- package/dist/lib/constructs/admin-dashboard.js +172 -0
- package/dist/lib/constructs/admin-dashboard.js.map +1 -0
- package/dist/lib/constructs/api.d.ts +17 -0
- package/dist/lib/constructs/api.d.ts.map +1 -0
- package/dist/lib/constructs/api.js +81 -0
- package/dist/lib/constructs/api.js.map +1 -0
- package/dist/lib/constructs/auth.d.ts +11 -0
- package/dist/lib/constructs/auth.d.ts.map +1 -0
- package/dist/lib/constructs/auth.js +18 -0
- package/dist/lib/constructs/auth.js.map +1 -0
- package/dist/lib/constructs/blob-store.d.ts +10 -0
- package/dist/lib/constructs/blob-store.d.ts.map +1 -0
- package/dist/lib/constructs/blob-store.js +31 -0
- package/dist/lib/constructs/blob-store.js.map +1 -0
- package/dist/lib/deploy-cli.d.ts +3 -0
- package/dist/lib/deploy-cli.d.ts.map +1 -0
- package/dist/lib/deploy-cli.js +49 -0
- package/dist/lib/deploy-cli.js.map +1 -0
- package/dist/lib/handler/index.d.ts +23 -0
- package/dist/lib/handler/index.d.ts.map +1 -0
- package/dist/lib/handler/index.js +276 -0
- package/dist/lib/handler/index.js.map +1 -0
- package/dist/lib/handler/index.ts +372 -0
- package/dist/lib/handler/logger.d.ts +16 -0
- package/dist/lib/handler/logger.d.ts.map +1 -0
- package/dist/lib/handler/logger.js +26 -0
- package/dist/lib/handler/logger.js.map +1 -0
- package/dist/lib/handler/logger.ts +36 -0
- package/dist/lib/handler/middleware/input-validation.d.ts +6 -0
- package/dist/lib/handler/middleware/input-validation.d.ts.map +1 -0
- package/dist/lib/handler/middleware/input-validation.js +36 -0
- package/dist/lib/handler/middleware/input-validation.js.map +1 -0
- package/dist/lib/handler/middleware/input-validation.ts +44 -0
- package/dist/lib/handler/middleware/rate-limit.d.ts +14 -0
- package/dist/lib/handler/middleware/rate-limit.d.ts.map +1 -0
- package/dist/lib/handler/middleware/rate-limit.js +94 -0
- package/dist/lib/handler/middleware/rate-limit.js.map +1 -0
- package/dist/lib/handler/middleware/rate-limit.ts +121 -0
- package/dist/lib/handler/middleware/ssh-auth.d.ts +48 -0
- package/dist/lib/handler/middleware/ssh-auth.d.ts.map +1 -0
- package/dist/lib/handler/middleware/ssh-auth.js +256 -0
- package/dist/lib/handler/middleware/ssh-auth.js.map +1 -0
- package/dist/lib/handler/middleware/ssh-auth.ts +300 -0
- package/dist/lib/handler/routes/audit.d.ts +24 -0
- package/dist/lib/handler/routes/audit.d.ts.map +1 -0
- package/dist/lib/handler/routes/audit.js +94 -0
- package/dist/lib/handler/routes/audit.js.map +1 -0
- package/dist/lib/handler/routes/audit.ts +101 -0
- package/dist/lib/handler/routes/blobs.d.ts +13 -0
- package/dist/lib/handler/routes/blobs.d.ts.map +1 -0
- package/dist/lib/handler/routes/blobs.js +298 -0
- package/dist/lib/handler/routes/blobs.js.map +1 -0
- package/dist/lib/handler/routes/blobs.ts +348 -0
- package/dist/lib/handler/routes/devices.d.ts +48 -0
- package/dist/lib/handler/routes/devices.d.ts.map +1 -0
- package/dist/lib/handler/routes/devices.js +394 -0
- package/dist/lib/handler/routes/devices.js.map +1 -0
- package/dist/lib/handler/routes/devices.ts +458 -0
- package/dist/lib/handler/routes/export.d.ts +9 -0
- package/dist/lib/handler/routes/export.d.ts.map +1 -0
- package/dist/lib/handler/routes/export.js +40 -0
- package/dist/lib/handler/routes/export.js.map +1 -0
- package/dist/lib/handler/routes/export.ts +55 -0
- package/dist/lib/handler/routes/github.d.ts +31 -0
- package/dist/lib/handler/routes/github.d.ts.map +1 -0
- package/dist/lib/handler/routes/github.js +118 -0
- package/dist/lib/handler/routes/github.js.map +1 -0
- package/dist/lib/handler/routes/github.ts +162 -0
- package/dist/lib/handler/routes/health.d.ts +6 -0
- package/dist/lib/handler/routes/health.d.ts.map +1 -0
- package/dist/lib/handler/routes/health.js +14 -0
- package/dist/lib/handler/routes/health.js.map +1 -0
- package/dist/lib/handler/routes/health.ts +10 -0
- package/dist/lib/handler/routes/invites.d.ts +24 -0
- package/dist/lib/handler/routes/invites.d.ts.map +1 -0
- package/dist/lib/handler/routes/invites.js +445 -0
- package/dist/lib/handler/routes/invites.js.map +1 -0
- package/dist/lib/handler/routes/invites.ts +527 -0
- package/dist/lib/handler/routes/notifications.d.ts +39 -0
- package/dist/lib/handler/routes/notifications.d.ts.map +1 -0
- package/dist/lib/handler/routes/notifications.js +150 -0
- package/dist/lib/handler/routes/notifications.js.map +1 -0
- package/dist/lib/handler/routes/notifications.ts +163 -0
- package/dist/lib/handler/routes/projects.d.ts +24 -0
- package/dist/lib/handler/routes/projects.d.ts.map +1 -0
- package/dist/lib/handler/routes/projects.js +47 -0
- package/dist/lib/handler/routes/projects.js.map +1 -0
- package/dist/lib/handler/routes/projects.ts +69 -0
- package/dist/lib/handler/routes/register.d.ts +19 -0
- package/dist/lib/handler/routes/register.d.ts.map +1 -0
- package/dist/lib/handler/routes/register.js +327 -0
- package/dist/lib/handler/routes/register.js.map +1 -0
- package/dist/lib/handler/routes/register.ts +363 -0
- package/dist/lib/handler/routes/restore.d.ts +9 -0
- package/dist/lib/handler/routes/restore.d.ts.map +1 -0
- package/dist/lib/handler/routes/restore.js +52 -0
- package/dist/lib/handler/routes/restore.js.map +1 -0
- package/dist/lib/handler/routes/restore.ts +73 -0
- package/dist/lib/handler/routes/revocation.d.ts +13 -0
- package/dist/lib/handler/routes/revocation.d.ts.map +1 -0
- package/dist/lib/handler/routes/revocation.js +63 -0
- package/dist/lib/handler/routes/revocation.js.map +1 -0
- package/dist/lib/handler/routes/revocation.ts +87 -0
- package/dist/lib/handler/routes/rotation.d.ts +24 -0
- package/dist/lib/handler/routes/rotation.d.ts.map +1 -0
- package/dist/lib/handler/routes/rotation.js +291 -0
- package/dist/lib/handler/routes/rotation.js.map +1 -0
- package/dist/lib/handler/routes/rotation.ts +336 -0
- package/dist/lib/handler/routes/tenants.d.ts +11 -0
- package/dist/lib/handler/routes/tenants.d.ts.map +1 -0
- package/dist/lib/handler/routes/tenants.js +181 -0
- package/dist/lib/handler/routes/tenants.js.map +1 -0
- package/dist/lib/handler/routes/tenants.ts +198 -0
- package/dist/lib/handler/routes/wrapped-key.d.ts +21 -0
- package/dist/lib/handler/routes/wrapped-key.d.ts.map +1 -0
- package/dist/lib/handler/routes/wrapped-key.js +76 -0
- package/dist/lib/handler/routes/wrapped-key.js.map +1 -0
- package/dist/lib/handler/routes/wrapped-key.ts +108 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +16 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +18 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../../../lib/handler/middleware/rate-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { DynamoDBDocumentClient, UpdateCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
|
|
3
|
+
export interface RateLimitResult {
|
|
4
|
+
allowed: boolean;
|
|
5
|
+
remaining: number;
|
|
6
|
+
retryAfter?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const LIMITS: Record<string, number> = {
|
|
10
|
+
PUT: 100,
|
|
11
|
+
GET: 1000,
|
|
12
|
+
DELETE: 100,
|
|
13
|
+
LIST: 100,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const WINDOW_SECONDS = 60;
|
|
17
|
+
|
|
18
|
+
export async function checkRateLimit(
|
|
19
|
+
tenantId: string,
|
|
20
|
+
operation: string,
|
|
21
|
+
ddb: DynamoDBDocumentClient,
|
|
22
|
+
tableName: string,
|
|
23
|
+
): Promise<RateLimitResult> {
|
|
24
|
+
const limit = LIMITS[operation] ?? 300;
|
|
25
|
+
const now = Math.floor(Date.now() / 1000);
|
|
26
|
+
const windowKey = Math.floor(now / WINDOW_SECONDS);
|
|
27
|
+
const ttl = windowKey * WINDOW_SECONDS + WINDOW_SECONDS + 120; // window + 2 min buffer
|
|
28
|
+
|
|
29
|
+
const result = await ddb.send(
|
|
30
|
+
new UpdateCommand({
|
|
31
|
+
TableName: tableName,
|
|
32
|
+
Key: {
|
|
33
|
+
PK: `RATE#${tenantId}`,
|
|
34
|
+
SK: `${operation}#${windowKey}`,
|
|
35
|
+
},
|
|
36
|
+
UpdateExpression: 'SET #count = if_not_exists(#count, :zero) + :one, #ttl = :ttl',
|
|
37
|
+
ExpressionAttributeNames: {
|
|
38
|
+
'#count': 'count',
|
|
39
|
+
'#ttl': 'ttl',
|
|
40
|
+
},
|
|
41
|
+
ExpressionAttributeValues: {
|
|
42
|
+
':zero': 0,
|
|
43
|
+
':one': 1,
|
|
44
|
+
':ttl': ttl,
|
|
45
|
+
},
|
|
46
|
+
ReturnValues: 'UPDATED_NEW',
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const currentCount = (result.Attributes?.['count'] as number) ?? 1;
|
|
51
|
+
const remaining = Math.max(0, limit - currentCount);
|
|
52
|
+
|
|
53
|
+
if (currentCount > limit) {
|
|
54
|
+
const windowEnd = (windowKey + 1) * WINDOW_SECONDS;
|
|
55
|
+
const retryAfter = Math.max(1, windowEnd - now);
|
|
56
|
+
return { allowed: false, remaining: 0, retryAfter };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { allowed: true, remaining };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Per-IP window sizes: LINK_CONFIRM uses 5-second windows, others use 1-second
|
|
63
|
+
const IP_WINDOW_SECONDS: Record<string, number> = {
|
|
64
|
+
LINK_CONFIRM: 5,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Rate limit by source IP for unauthenticated endpoints (registration, contact).
|
|
69
|
+
* Default: 1 request per second per IP. LINK_CONFIRM: 1 request per 5 seconds.
|
|
70
|
+
*/
|
|
71
|
+
export async function checkIpRateLimit(
|
|
72
|
+
sourceIp: string,
|
|
73
|
+
operation: string,
|
|
74
|
+
ddb: DynamoDBDocumentClient,
|
|
75
|
+
tableName: string,
|
|
76
|
+
limit = 1,
|
|
77
|
+
): Promise<RateLimitResult> {
|
|
78
|
+
const now = Math.floor(Date.now() / 1000);
|
|
79
|
+
const windowSec = IP_WINDOW_SECONDS[operation] ?? 1;
|
|
80
|
+
const windowKey = Math.floor(now / windowSec);
|
|
81
|
+
const ttl = now + 120;
|
|
82
|
+
|
|
83
|
+
const result = await ddb.send(
|
|
84
|
+
new UpdateCommand({
|
|
85
|
+
TableName: tableName,
|
|
86
|
+
Key: {
|
|
87
|
+
PK: `RATE#IP#${sourceIp}`,
|
|
88
|
+
SK: `${operation}#${windowKey}`,
|
|
89
|
+
},
|
|
90
|
+
UpdateExpression: 'SET #count = if_not_exists(#count, :zero) + :one, #ttl = :ttl',
|
|
91
|
+
ExpressionAttributeNames: {
|
|
92
|
+
'#count': 'count',
|
|
93
|
+
'#ttl': 'ttl',
|
|
94
|
+
},
|
|
95
|
+
ExpressionAttributeValues: {
|
|
96
|
+
':zero': 0,
|
|
97
|
+
':one': 1,
|
|
98
|
+
':ttl': ttl,
|
|
99
|
+
},
|
|
100
|
+
ReturnValues: 'UPDATED_NEW',
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const currentCount = (result.Attributes?.['count'] as number) ?? 1;
|
|
105
|
+
if (currentCount > limit) {
|
|
106
|
+
const windowEnd = (windowKey + 1) * windowSec;
|
|
107
|
+
const retryAfter = Math.max(1, windowEnd - now);
|
|
108
|
+
return { allowed: false, remaining: 0, retryAfter };
|
|
109
|
+
}
|
|
110
|
+
return { allowed: true, remaining: Math.max(0, limit - currentCount) };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function rateLimitHeaders(result: RateLimitResult): Record<string, string> {
|
|
114
|
+
const headers: Record<string, string> = {
|
|
115
|
+
'X-RateLimit-Remaining': String(result.remaining),
|
|
116
|
+
};
|
|
117
|
+
if (!result.allowed && result.retryAfter !== undefined) {
|
|
118
|
+
headers['Retry-After'] = String(result.retryAfter);
|
|
119
|
+
}
|
|
120
|
+
return headers;
|
|
121
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
export interface AuthResult {
|
|
3
|
+
tenantId: string;
|
|
4
|
+
publicKey: string;
|
|
5
|
+
fingerprint: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class AuthError extends Error {
|
|
8
|
+
readonly statusCode: number;
|
|
9
|
+
constructor(message: string, statusCode: number);
|
|
10
|
+
}
|
|
11
|
+
interface ParsedAuthHeader {
|
|
12
|
+
signature: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
sequence: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse the new SSH-Signature authorization scheme.
|
|
18
|
+
*
|
|
19
|
+
* Headers:
|
|
20
|
+
* Authorization: SSH-Signature <base64-signature>
|
|
21
|
+
* X-ChaosKB-Timestamp: <ISO 8601>
|
|
22
|
+
* X-ChaosKB-Sequence: <monotonic counter>
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseAuthHeaders(headers: Record<string, string>): ParsedAuthHeader;
|
|
25
|
+
/** Compute SSH key fingerprint (SHA-256 of raw public key, base64). */
|
|
26
|
+
export declare function fingerprintFromPublicKey(publicKeyBase64: string): string;
|
|
27
|
+
export declare function verifyTimestamp(timestamp: string): void;
|
|
28
|
+
export declare function buildCanonicalString(method: string, path: string, timestamp: string, sequence: number, body?: string | null): string;
|
|
29
|
+
/**
|
|
30
|
+
* Check and update the per-device sequence counter for replay protection.
|
|
31
|
+
*
|
|
32
|
+
* Uses a DynamoDB conditional write: only succeeds if the new sequence
|
|
33
|
+
* is strictly greater than the stored highest-seen sequence.
|
|
34
|
+
*/
|
|
35
|
+
export declare function checkSequence(ddb: DynamoDBDocumentClient, tableName: string, tenantId: string, fingerprint: string, sequence: number): Promise<void>;
|
|
36
|
+
export declare function verifyEd25519Signature(publicKeyBase64: string, canonicalString: string, signatureBase64: string): boolean;
|
|
37
|
+
export declare function authenticateRequest(event: {
|
|
38
|
+
requestContext: {
|
|
39
|
+
http: {
|
|
40
|
+
method: string;
|
|
41
|
+
path: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
headers: Record<string, string>;
|
|
45
|
+
body?: string | null;
|
|
46
|
+
}, ddb: DynamoDBDocumentClient, tableName: string): Promise<AuthResult>;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=ssh-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-auth.d.ts","sourceRoot":"","sources":["../../../../lib/handler/middleware/ssh-auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAA+B,MAAM,uBAAuB,CAAC;AAG5F,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,SAAU,SAAQ,KAAK;aAGhB,UAAU,EAAE,MAAM;gBADlC,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM;CAKrC;AAED,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAKD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,gBAAgB,CA4BlF;AA6BD,uEAAuE;AACvE,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAWvD;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,MAAM,CAKR;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,sBAAsB,EAC3B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED,wBAAgB,sBAAsB,CACpC,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,GACtB,OAAO,CAqBT;AAOD,wBAAsB,mBAAmB,CACvC,KAAK,EAAE;IACL,cAAc,EAAE;QAAE,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC3D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,EACD,GAAG,EAAE,sBAAsB,EAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,CAAC,CA2DrB"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AuthError = void 0;
|
|
37
|
+
exports.parseAuthHeaders = parseAuthHeaders;
|
|
38
|
+
exports.fingerprintFromPublicKey = fingerprintFromPublicKey;
|
|
39
|
+
exports.verifyTimestamp = verifyTimestamp;
|
|
40
|
+
exports.buildCanonicalString = buildCanonicalString;
|
|
41
|
+
exports.checkSequence = checkSequence;
|
|
42
|
+
exports.verifyEd25519Signature = verifyEd25519Signature;
|
|
43
|
+
exports.authenticateRequest = authenticateRequest;
|
|
44
|
+
const crypto = __importStar(require("crypto"));
|
|
45
|
+
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
46
|
+
const logger_js_1 = require("../logger.js");
|
|
47
|
+
class AuthError extends Error {
|
|
48
|
+
statusCode;
|
|
49
|
+
constructor(message, statusCode) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.statusCode = statusCode;
|
|
52
|
+
this.name = 'AuthError';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.AuthError = AuthError;
|
|
56
|
+
/** Timestamp tolerance: 30 seconds (replay protection is primarily sequence-based). */
|
|
57
|
+
const TIMESTAMP_TOLERANCE_MS = 30 * 1000;
|
|
58
|
+
/**
|
|
59
|
+
* Parse the new SSH-Signature authorization scheme.
|
|
60
|
+
*
|
|
61
|
+
* Headers:
|
|
62
|
+
* Authorization: SSH-Signature <base64-signature>
|
|
63
|
+
* X-ChaosKB-Timestamp: <ISO 8601>
|
|
64
|
+
* X-ChaosKB-Sequence: <monotonic counter>
|
|
65
|
+
*/
|
|
66
|
+
function parseAuthHeaders(headers) {
|
|
67
|
+
const authHeader = headers['authorization'] || headers['Authorization'];
|
|
68
|
+
if (!authHeader) {
|
|
69
|
+
throw new AuthError('Missing Authorization header', 401);
|
|
70
|
+
}
|
|
71
|
+
if (!authHeader.startsWith('SSH-Signature ')) {
|
|
72
|
+
// Support legacy format during migration
|
|
73
|
+
if (authHeader.startsWith('ChaosKB-SSH ')) {
|
|
74
|
+
return parseLegacyAuthHeader(authHeader, headers);
|
|
75
|
+
}
|
|
76
|
+
throw new AuthError('Invalid authorization scheme', 401);
|
|
77
|
+
}
|
|
78
|
+
const signature = authHeader.slice('SSH-Signature '.length);
|
|
79
|
+
const timestamp = headers['x-chaoskb-timestamp'] || headers['X-ChaosKB-Timestamp'];
|
|
80
|
+
const sequenceStr = headers['x-chaoskb-sequence'] || headers['X-ChaosKB-Sequence'];
|
|
81
|
+
if (!signature || !timestamp) {
|
|
82
|
+
throw new AuthError('Missing required headers (X-ChaosKB-Timestamp)', 401);
|
|
83
|
+
}
|
|
84
|
+
const sequence = sequenceStr ? parseInt(sequenceStr, 10) : 0;
|
|
85
|
+
if (isNaN(sequence)) {
|
|
86
|
+
throw new AuthError('Invalid sequence number', 401);
|
|
87
|
+
}
|
|
88
|
+
return { signature, timestamp, sequence };
|
|
89
|
+
}
|
|
90
|
+
/** Parse legacy ChaosKB-SSH format for backwards compatibility. */
|
|
91
|
+
function parseLegacyAuthHeader(header, headers) {
|
|
92
|
+
const params = header.slice('ChaosKB-SSH '.length);
|
|
93
|
+
const fields = {};
|
|
94
|
+
for (const part of params.split(', ')) {
|
|
95
|
+
const eqIndex = part.indexOf('=');
|
|
96
|
+
if (eqIndex === -1)
|
|
97
|
+
continue;
|
|
98
|
+
fields[part.slice(0, eqIndex)] = part.slice(eqIndex + 1);
|
|
99
|
+
}
|
|
100
|
+
if (!fields['sig'] || !fields['ts']) {
|
|
101
|
+
throw new AuthError('Missing required authorization fields', 401);
|
|
102
|
+
}
|
|
103
|
+
const sequenceStr = headers['x-chaoskb-sequence'] || headers['X-ChaosKB-Sequence'];
|
|
104
|
+
return {
|
|
105
|
+
signature: fields['sig'],
|
|
106
|
+
timestamp: fields['ts'],
|
|
107
|
+
sequence: sequenceStr ? parseInt(sequenceStr, 10) : 0,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Compute SSH key fingerprint (SHA-256 of raw public key, base64). */
|
|
111
|
+
function fingerprintFromPublicKey(publicKeyBase64) {
|
|
112
|
+
return crypto.createHash('sha256').update(Buffer.from(publicKeyBase64, 'base64')).digest('base64');
|
|
113
|
+
}
|
|
114
|
+
function verifyTimestamp(timestamp) {
|
|
115
|
+
const requestTime = new Date(timestamp).getTime();
|
|
116
|
+
if (isNaN(requestTime)) {
|
|
117
|
+
throw new AuthError('Invalid timestamp format', 401);
|
|
118
|
+
}
|
|
119
|
+
const now = Date.now();
|
|
120
|
+
const diff = Math.abs(now - requestTime);
|
|
121
|
+
if (diff > TIMESTAMP_TOLERANCE_MS) {
|
|
122
|
+
throw new AuthError('Request timestamp expired', 401);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function buildCanonicalString(method, path, timestamp, sequence, body) {
|
|
126
|
+
const bodyHash = body
|
|
127
|
+
? crypto.createHash('sha256').update(body).digest('hex')
|
|
128
|
+
: '';
|
|
129
|
+
return `chaoskb-auth\n${method} ${path}\n${timestamp}\n${sequence}\n${bodyHash}`;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check and update the per-device sequence counter for replay protection.
|
|
133
|
+
*
|
|
134
|
+
* Uses a DynamoDB conditional write: only succeeds if the new sequence
|
|
135
|
+
* is strictly greater than the stored highest-seen sequence.
|
|
136
|
+
*/
|
|
137
|
+
async function checkSequence(ddb, tableName, tenantId, fingerprint, sequence) {
|
|
138
|
+
if (sequence <= 0) {
|
|
139
|
+
throw new AuthError('Sequence number must be positive', 401);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
await ddb.send(new lib_dynamodb_1.UpdateCommand({
|
|
143
|
+
TableName: tableName,
|
|
144
|
+
Key: {
|
|
145
|
+
PK: `TENANT#${tenantId}`,
|
|
146
|
+
SK: `SEQUENCE#${fingerprint}`,
|
|
147
|
+
},
|
|
148
|
+
UpdateExpression: 'SET highestSeq = :new',
|
|
149
|
+
ConditionExpression: 'attribute_not_exists(highestSeq) OR highestSeq < :new',
|
|
150
|
+
ExpressionAttributeValues: {
|
|
151
|
+
':new': sequence,
|
|
152
|
+
},
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (error &&
|
|
157
|
+
typeof error === 'object' &&
|
|
158
|
+
'name' in error &&
|
|
159
|
+
error.name === 'ConditionalCheckFailedException') {
|
|
160
|
+
logger_js_1.logger.warn('Replay detected: sequence number already seen', {
|
|
161
|
+
tenantId,
|
|
162
|
+
fingerprint,
|
|
163
|
+
sequence,
|
|
164
|
+
});
|
|
165
|
+
throw new AuthError('Replay detected: sequence number already used', 401);
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function verifyEd25519Signature(publicKeyBase64, canonicalString, signatureBase64) {
|
|
171
|
+
try {
|
|
172
|
+
const publicKeyBuffer = Buffer.from(publicKeyBase64, 'base64');
|
|
173
|
+
const signatureBuffer = Buffer.from(signatureBase64, 'base64');
|
|
174
|
+
const data = Buffer.from(canonicalString);
|
|
175
|
+
// Create Ed25519 public key object from raw bytes
|
|
176
|
+
const keyObject = crypto.createPublicKey({
|
|
177
|
+
key: Buffer.concat([
|
|
178
|
+
// Ed25519 DER prefix for a 32-byte public key
|
|
179
|
+
Buffer.from('302a300506032b6570032100', 'hex'),
|
|
180
|
+
publicKeyBuffer,
|
|
181
|
+
]),
|
|
182
|
+
format: 'der',
|
|
183
|
+
type: 'spki',
|
|
184
|
+
});
|
|
185
|
+
return crypto.verify(null, data, keyObject, signatureBuffer);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function tenantIdFromPublicKey(publicKeyBase64) {
|
|
192
|
+
const hash = crypto.createHash('sha256').update(publicKeyBase64).digest('hex');
|
|
193
|
+
return hash.slice(0, 32);
|
|
194
|
+
}
|
|
195
|
+
async function authenticateRequest(event, ddb, tableName) {
|
|
196
|
+
const parsed = parseAuthHeaders(event.headers);
|
|
197
|
+
verifyTimestamp(parsed.timestamp);
|
|
198
|
+
// Look up the public key by querying tenant META records
|
|
199
|
+
// The public key is identified from the request — we need to find which tenant it belongs to
|
|
200
|
+
// For now, extract from legacy header or from a separate header
|
|
201
|
+
const publicKey = extractPublicKey(event.headers);
|
|
202
|
+
const tenantId = tenantIdFromPublicKey(publicKey);
|
|
203
|
+
const fingerprint = fingerprintFromPublicKey(publicKey);
|
|
204
|
+
// Look up the registered public key in DynamoDB
|
|
205
|
+
const result = await ddb.send(new lib_dynamodb_1.QueryCommand({
|
|
206
|
+
TableName: tableName,
|
|
207
|
+
KeyConditionExpression: 'PK = :pk AND SK = :sk',
|
|
208
|
+
ExpressionAttributeValues: {
|
|
209
|
+
':pk': `TENANT#${tenantId}`,
|
|
210
|
+
':sk': 'META',
|
|
211
|
+
},
|
|
212
|
+
Limit: 1,
|
|
213
|
+
}));
|
|
214
|
+
if (!result.Items || result.Items.length === 0) {
|
|
215
|
+
throw new AuthError('Unknown public key', 401);
|
|
216
|
+
}
|
|
217
|
+
const tenant = result.Items[0];
|
|
218
|
+
if (tenant['publicKey'] !== publicKey) {
|
|
219
|
+
throw new AuthError('Public key mismatch', 401);
|
|
220
|
+
}
|
|
221
|
+
// Verify the SSH signature against the canonical string (includes sequence)
|
|
222
|
+
const canonicalString = buildCanonicalString(event.requestContext.http.method, event.requestContext.http.path, parsed.timestamp, parsed.sequence, event.body);
|
|
223
|
+
const valid = verifyEd25519Signature(publicKey, canonicalString, parsed.signature);
|
|
224
|
+
if (!valid) {
|
|
225
|
+
logger_js_1.logger.warn('Signature verification failed', { tenantId });
|
|
226
|
+
throw new AuthError('Invalid signature', 401);
|
|
227
|
+
}
|
|
228
|
+
// Check sequence number for replay protection (after signature verification)
|
|
229
|
+
if (parsed.sequence > 0) {
|
|
230
|
+
await checkSequence(ddb, tableName, tenantId, fingerprint, parsed.sequence);
|
|
231
|
+
}
|
|
232
|
+
return { tenantId, publicKey, fingerprint };
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Extract the public key from request headers.
|
|
236
|
+
* New format uses X-ChaosKB-PublicKey header; legacy embeds it in the auth header.
|
|
237
|
+
*/
|
|
238
|
+
function extractPublicKey(headers) {
|
|
239
|
+
// New header format
|
|
240
|
+
const pubKeyHeader = headers['x-chaoskb-publickey'] || headers['X-ChaosKB-PublicKey'];
|
|
241
|
+
if (pubKeyHeader)
|
|
242
|
+
return pubKeyHeader;
|
|
243
|
+
// Legacy format: ChaosKB-SSH pubkey=..., ts=..., sig=...
|
|
244
|
+
const authHeader = headers['authorization'] || headers['Authorization'];
|
|
245
|
+
if (authHeader?.startsWith('ChaosKB-SSH ')) {
|
|
246
|
+
const params = authHeader.slice('ChaosKB-SSH '.length);
|
|
247
|
+
for (const part of params.split(', ')) {
|
|
248
|
+
const eqIndex = part.indexOf('=');
|
|
249
|
+
if (eqIndex !== -1 && part.slice(0, eqIndex) === 'pubkey') {
|
|
250
|
+
return part.slice(eqIndex + 1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
throw new AuthError('Missing public key in request headers', 401);
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=ssh-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-auth.js","sourceRoot":"","sources":["../../../../lib/handler/middleware/ssh-auth.ts"],"names":[],"mappings":"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"}
|