@vizamodo/aws-sts-core 0.1.45 → 0.1.49

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,61 @@ 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
+ // --- MINIMAL DER WALK: extract serial number ---
62
+ let certSerialDec;
63
+ try {
64
+ const der = new Uint8Array(base64ToArrayBuffer(normalizedCert));
65
+ let offset = 0;
66
+ // helper: read DER length (short + long form)
67
+ function readLen() {
68
+ const b = der[offset++];
69
+ if ((b & 0x80) === 0)
70
+ return b;
71
+ const n = b & 0x7f;
72
+ let len = 0;
73
+ for (let i = 0; i < n; i++) {
74
+ len = (len << 8) | der[offset++];
75
+ }
76
+ return len;
77
+ }
78
+ // Certificate SEQUENCE
79
+ if (der[offset++] !== 0x30)
80
+ throw new Error("bad cert");
81
+ readLen();
82
+ // TBSCertificate SEQUENCE
83
+ if (der[offset++] !== 0x30)
84
+ throw new Error("bad tbs");
85
+ readLen();
86
+ // Optional version [0] EXPLICIT (0xa0)
87
+ if (der[offset] === 0xa0) {
88
+ offset++; // tag
89
+ const vLen = readLen(); // length
90
+ offset += vLen; // skip full version block
91
+ }
92
+ // SerialNumber INTEGER
93
+ if (der[offset++] !== 0x02)
94
+ throw new Error("bad serial tag");
95
+ const serialLen = readLen();
96
+ const serial = der.slice(offset, offset + serialLen);
97
+ certSerialDec = BigInt("0x" +
98
+ Array.from(serial)
99
+ .map(b => b.toString(16).padStart(2, "0"))
100
+ .join("")).toString();
101
+ }
102
+ catch (e) {
103
+ console.error("[issueAwsCredentials] Failed to parse cert serial", e);
75
104
  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)
105
+ }
106
+ // 4. Tính toán Signature
83
107
  const baseHeaders = {
84
108
  "content-type": "application/json",
85
109
  "host": host,
@@ -103,14 +127,14 @@ export async function issueAwsCredentials(input) {
103
127
  canonicalRequestHash: await sha256Hex(canonicalRequest),
104
128
  });
105
129
  const signatureHex = await signStringToSign(stringToSign, signingKey);
106
- // 5. Build Headers gửi đi (KHÔNG gửi Host header thủ công trong fetch)
130
+ // 5. Build Authorization Header với số Serial (DECIMAL)
107
131
  const finalHeaders = new Headers({
108
132
  "Content-Type": "application/json",
109
133
  "X-Amz-Date": amzDate,
110
134
  "X-Amz-X509": normalizedCert,
111
- "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${certSerialHex}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
135
+ "Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${certSerialDec}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
112
136
  });
113
- // 6. Execution (fetch)
137
+ // 6. Execution
114
138
  try {
115
139
  const res = await fetch(`https://${host}${path}`, {
116
140
  method: "POST",
@@ -119,51 +143,11 @@ export async function issueAwsCredentials(input) {
119
143
  });
120
144
  if (!res.ok) {
121
145
  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]", {
146
+ // Debug logs
147
+ console.error("[aws-rejected] Request details", {
137
148
  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
- }
149
+ response: errorBody,
150
+ serial: certSerialDec
167
151
  });
168
152
  throw new InternalError("aws_rejected");
169
153
  }
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.49",
4
4
  "description": "Pure AWS STS + SigV4 (X509 Roles Anywhere) core logic",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",