@vizamodo/aws-sts-core 0.1.45 → 0.1.47

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.
@@ -1,9 +1,6 @@
1
1
  import type { AwsCredentialResult } from "../types";
2
2
  /**
3
3
  * Issue short-lived AWS credentials via Roles Anywhere.
4
- * Preserve:
5
- * - TTL per profile
6
- * - isolate-level signing material cache
7
4
  */
8
5
  export declare function issueAwsCredentials(input: {
9
6
  roleArn: string;
package/dist/sts/issue.js CHANGED
@@ -23,7 +23,7 @@ async function getSigningMaterial(input) {
23
23
  function resolveSessionTtlByProfile(profile) {
24
24
  switch (profile) {
25
25
  case "HubConsoleReadOnly":
26
- return 1 * 60 * 60; // 12h
26
+ return 1 * 60 * 60; // 1h
27
27
  case "HubBillingReadonly":
28
28
  case "HubBillingAdmin":
29
29
  return 2 * 60 * 60; // 2h
@@ -36,14 +36,10 @@ function resolveSessionTtlByProfile(profile) {
36
36
  }
37
37
  /**
38
38
  * Issue short-lived AWS credentials via Roles Anywhere.
39
- * Preserve:
40
- * - TTL per profile
41
- * - isolate-level signing material cache
42
39
  */
43
40
  export async function issueAwsCredentials(input) {
44
41
  const { roleArn, profileArn, trustAnchorArn, region, certBase64, privateKeyPkcs8Base64, profile, } = input;
45
42
  // 1. Kiểm tra đầu vào & Fix TTL an toàn
46
- // AWS mặc định chỉ cho phép 3600s, hãy fix cứng 1h để loại trừ lỗi IAM
47
43
  const sessionTtl = Math.min(resolveSessionTtlByProfile(profile), 3600);
48
44
  const { signingKey } = await getSigningMaterial({ certBase64, privateKeyPkcs8Base64 });
49
45
  // 2. Setup constants
@@ -53,33 +49,46 @@ export async function issueAwsCredentials(input) {
53
49
  const amzDate = now.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/[:-]/g, "");
54
50
  const dateStamp = amzDate.slice(0, 8);
55
51
  const service = "rolesanywhere";
56
- // 3. Chuẩn bị Body & Cert (base64-encoded DER, không marker, không xuống dòng)
52
+ // 3. Chuẩn bị Body & Cert
57
53
  const body = JSON.stringify({ trustAnchorArn, profileArn, roleArn, durationSeconds: sessionTtl });
58
54
  const payloadHash = await sha256Hex(body);
59
- // AWS Roles Anywhere requires base64-encoded DER (NO PEM markers, NO newlines)
60
- const normalizedCert = certBase64.replace(/\s+/g, "");
61
- // Extract certificate serial number (hex, uppercase) for Credential field
62
- const certDer = base64ToArrayBuffer(normalizedCert);
63
- // Extract certificate serial number from DER (ASN.1)
64
- const derBytes = new Uint8Array(certDer);
65
- // Very small ASN.1 parser: find first INTEGER after TBSCertificate
66
- // Assumes standard X.509 v3 structure (safe for our controlled certs)
67
- let offset = 0;
68
- if (derBytes[offset++] !== 0x30)
69
- throw new InternalError("invalid_cert_der");
70
- offset += derBytes[offset] & 0x80 ? (derBytes[offset] & 0x7f) + 1 : 1;
71
- if (derBytes[offset++] !== 0x30)
72
- throw new InternalError("invalid_cert_der");
73
- offset += derBytes[offset] & 0x80 ? (derBytes[offset] & 0x7f) + 1 : 1;
74
- if (derBytes[offset++] !== 0x02)
55
+ // --- REFACTOR: Làm sạch chứng chỉ chặt chẽ ---
56
+ const normalizedCert = certBase64
57
+ .replace(/-----BEGIN CERTIFICATE-----/g, "")
58
+ .replace(/-----END CERTIFICATE-----/g, "")
59
+ .replace(/\s+/g, "");
60
+ // --- REFACTOR: Parser ASN.1 an toàn để lấy Serial Number (DECIMAL) ---
61
+ let certSerialDec;
62
+ try {
63
+ const certDer = base64ToArrayBuffer(normalizedCert);
64
+ const derBytes = new Uint8Array(certDer);
65
+ let offset = 0;
66
+ // X509v3 structure check
67
+ if (derBytes[offset++] !== 0x30)
68
+ throw new Error("Not a sequence");
69
+ offset += derBytes[offset] & 0x80 ? (derBytes[offset] & 0x7f) + 1 : 1;
70
+ if (derBytes[offset++] !== 0x30)
71
+ throw new Error("Not a sequence");
72
+ offset += derBytes[offset] & 0x80 ? (derBytes[offset] & 0x7f) + 1 : 1;
73
+ // INTEGER (Serial Number)
74
+ if (derBytes[offset++] !== 0x02)
75
+ throw new Error("Not an integer");
76
+ const serialLength = derBytes[offset++];
77
+ // Bounds check
78
+ if (offset + serialLength > derBytes.length)
79
+ throw new Error("Bounds error");
80
+ const serialBytes = derBytes.slice(offset, offset + serialLength);
81
+ // Convert to Decimal string
82
+ certSerialDec = BigInt("0x" + Array.from(serialBytes)
83
+ .map((b) => b.toString(16).padStart(2, "0"))
84
+ .join(""))
85
+ .toString();
86
+ }
87
+ catch (e) {
88
+ console.error("[issueAwsCredentials] Failed to parse cert serial", e);
75
89
  throw new InternalError("invalid_cert_der");
76
- const serialLength = derBytes[offset++];
77
- const serialBytes = derBytes.slice(offset, offset + serialLength);
78
- const certSerialHex = Array.from(serialBytes)
79
- .map((b) => b.toString(16).padStart(2, "0"))
80
- .join("")
81
- .toUpperCase();
82
- // 4. Tính toán Signature (CẦN Host trong Canonical Request)
90
+ }
91
+ // 4. Tính toán Signature
83
92
  const baseHeaders = {
84
93
  "content-type": "application/json",
85
94
  "host": host,
@@ -103,14 +112,14 @@ export async function issueAwsCredentials(input) {
103
112
  canonicalRequestHash: await sha256Hex(canonicalRequest),
104
113
  });
105
114
  const signatureHex = await signStringToSign(stringToSign, signingKey);
106
- // 5. Build Headers gửi đi (KHÔNG gửi Host header thủ công trong fetch)
115
+ // 5. Build Authorization Header với số Serial (DECIMAL)
107
116
  const finalHeaders = new Headers({
108
117
  "Content-Type": "application/json",
109
118
  "X-Amz-Date": amzDate,
110
119
  "X-Amz-X509": normalizedCert,
111
- "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${certSerialHex}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
120
+ "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${certSerialDec}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
112
121
  });
113
- // 6. Execution (fetch)
122
+ // 6. Execution
114
123
  try {
115
124
  const res = await fetch(`https://${host}${path}`, {
116
125
  method: "POST",
@@ -119,51 +128,11 @@ export async function issueAwsCredentials(input) {
119
128
  });
120
129
  if (!res.ok) {
121
130
  const errorBody = await res.text();
122
- // ---- Local curl debug command (FULL MATERIAL - ROTATE AFTER DEBUG) ----
123
- const localCurlCommand = [
124
- `curl -X POST "https://${host}${path}" \\`,
125
- ` -H "Content-Type: application/json" \\`,
126
- ` -H "X-Amz-Date: ${amzDate}" \\`,
127
- ` -H "X-Amz-X509: ${normalizedCert}" \\`,
128
- ` -H "Authorization: AWS4-X509-ECDSA-SHA256 Credential=${certSerialHex}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}" \\`,
129
- ` -d '${body}'`
130
- ].join("\n");
131
- console.error("[aws-rejected][LOCAL-CURL-COMMAND]", localCurlCommand);
132
- console.error("[aws-rejected][RAW-SIGNING-MATERIAL]", {
133
- certBase64,
134
- privateKeyPkcs8Base64
135
- });
136
- console.error("[aws-rejected][FULL-REQUEST-DUMP]", {
131
+ // Debug logs
132
+ console.error("[aws-rejected] Request details", {
137
133
  status: res.status,
138
- statusText: res.statusText,
139
- awsResponse: errorBody,
140
- // ---- Input parameters ----
141
- roleArn,
142
- profileArn,
143
- trustAnchorArn,
144
- region,
145
- profile,
146
- // ---- Derived signing values ----
147
- host,
148
- path,
149
- amzDate,
150
- dateStamp,
151
- credentialScope,
152
- signedHeaders,
153
- signatureHex,
154
- // ---- Body & payload ----
155
- requestBody: body,
156
- payloadHash,
157
- // ---- Canonical ----
158
- canonicalRequest,
159
- stringToSign,
160
- // ---- Headers actually sent ----
161
- sentHeaders: {
162
- "Content-Type": "application/json",
163
- "X-Amz-Date": amzDate,
164
- "X-Amz-X509": normalizedCert,
165
- "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${certSerialHex}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
166
- }
134
+ response: errorBody,
135
+ serial: certSerialDec
167
136
  });
168
137
  throw new InternalError("aws_rejected");
169
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizamodo/aws-sts-core",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
4
4
  "description": "Pure AWS STS + SigV4 (X509 Roles Anywhere) core logic",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",