@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.
- package/dist/sts/issue.d.ts +0 -3
- package/dist/sts/issue.js +45 -76
- package/package.json +1 -1
package/dist/sts/issue.d.ts
CHANGED
|
@@ -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; //
|
|
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
|
|
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
|
-
//
|
|
60
|
-
const normalizedCert = certBase64
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
|
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=${
|
|
120
|
+
"Authorization": `AWS4-X509-ECDSA-SHA256 Credential=${certSerialDec}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signatureHex}`
|
|
112
121
|
});
|
|
113
|
-
// 6. Execution
|
|
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
|
-
//
|
|
123
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
}
|