diff --git a/components/component-alibaba-cloud-sts.js b/components/component-alibaba-cloud-sts.js new file mode 100644 index 0000000..18a2863 --- /dev/null +++ b/components/component-alibaba-cloud-sts.js @@ -0,0 +1,116 @@ +var ALIBABA_CLOUD_PKCS7_URL = "http://100.100.100.200/2016-01-01/dynamic/instance-identity/pkcs7"; +var ASSUME_ROLE_URL = "https://hatter.ink/cloud/alibaba_cloud/assume_role.json"; +var ASSUME_ROLE_PKCS7_URL = "https://hatter.ink/cloud/alibaba_cloud/assume_role_pkcs7.json"; + +var localEncryption = require('https://hatter.ink/script/get/@1/component-local-encryption.js', null, + 'f910156414a71339c87aec6f013f7ab504dfc2992e94d4d6e66fa7864a6f54a8'); + +function fetchAlibabaCloudStsAuto() { + var System = Packages.java.lang.System; + var alibabaCloudStsType = System.getenv('ALIBABA_CLOUD_STS_TYPE'); + var alibabaCloudStsRoleArn = System.getenv('ALIBABA_CLOUD_STS_ROLE_ARN'); + var alibabaCloutStsSlot = System.getenv('ALIBABA_CLOUD_STS_SLOT'); + return fetchAlibabaCloudStsWithCache({ + type: alibabaCloudStsType, + roleArn: alibabaCloudStsRoleArn, + slot: alibabaCloutStsSlot + }); +} + +// var sts = fetchAlibabaCloudStsWithCache({ +// type: 'piv', +// roleArn: 'acs:ram::1747527333918361:role/test-assertion-assume-role', +// slot: 'r3' +// }); +// -----OR----- +// var sts = fetchAlibabaCloudStsWithCache({ +// type: 'alibaba_cloud_instance', +// roleArn: 'acs:ram::1747527333918361:role/test-assertion-assume-role' +// }); +function fetchAlibabaCloudStsWithCache(request) { + var cacheKey = (request.type || 'type-na') + + '_' + (request.roleArn || 'role-na') + + '_' + (request.slot || 'slot-na'); + cacheKey = cacheKey.replace(/[^a-zA-Z0-9:-_]/g, '-'); + + var alibabaCloudStsDir = $$.rFile('~/.jssp/cache/.alibabacloudsts').newDirIfNotExists(); + var stsFile = alibabaCloudStsDir.file(cacheKey); + var sts = null; + if (stsFile.exists()) { + try { + var tempSts = JSON.parse(localEncryption.decrypt(stsFile.string().trim())); + if (tempSts.expiration) { + var expireMillis = new Date(tempSts.expiration).getTime(); + if (expireMillis > (new Date().getTime() + (60 * 1000))) { + sts = tempSts; + } + } + // TODO check validity + } catch (e) { + // IGNORE + } + } + if (sts == null) { + sts = fetchAlibabaCloudSts(request); + stsFile.write(localEncryption.encrypt(JSON.stringify(sts))); + } + return sts; +} + +function fetchAlibabaCloudSts(request) { + if (request.type == "alibaba_cloud_instance") { + return fetchAlibabaCloudStsByAlibabaCloudInstanceIdentity(request.roleArn); + } + if (request.type == "piv") { + return fetchAlibabaCloudStsByCardCliPiv(request.roleArn, request.slot); + } + throw 'Not supported request type: ' + request.type; +} + +function fetchAlibabaCloudStsByAlibabaCloudInstanceIdentity(roleArn) { + var curentMillis = $$.date().millis(); + var uniqId = 'uid_' + curentMillis; + var aud = 'arn:hatter.ink:alibaba_cloud:action:assume_role'; + var pkcs7Audience = aud + '|' + roleArn + '|' + uniqId + '|' + curentMillis; + + var pkcs7 = $$.httpRequest() + .url(ALIBABA_CLOUD_PKCS7_URL + '?audience=' + encodeURIComponent(pkcs7Audience)) + .addHeader('User-Agent', 'runjs-component/1.0') + .get().toString().trim(); + + var sts = $$.httpRequest() + .url(ASSUME_ROLE_PKCS7_URL + '?type=cli') + .addHeader('User-Agent', 'runjs-component/1.0') + .addHeader('Authorization', 'PKCS7 ' + pkcs7) + .get().toString().trim(); + + return JSON.parse(sts).data; +} + +function fetchAlibabaCloudStsByCardCliPiv(roleArn, slot) { + var result = $$.shell().commands('card-cli', 'sign-jwt', + '-s', slot, + '-C', 'sub:' + roleArn, + '-C', 'aud:arn:hatter.ink:alibaba_cloud:action:assume_role', + '-K', slot, + '--jti', '--validity', '10m', '--json').start(); + var out = result[0].string().trim(); + var err = result[1].string().trim(); + if ($STR(out.trim()) == '') { + throw 'Run card-cli error: ' + err; + } + var jwt_token = JSON.parse(out).token; + + var sts = $$.httpRequest() + .url(ASSUME_ROLE_URL + '?type=cli&assertion=' + encodeURIComponent(jwt_token)) + .addHeader('User-Agent', 'runjs-component/1.0') + .get().toString().trim(); + + return JSON.parse(sts).data; +} + +if (typeof exports == 'object') { + exports.fetchAlibabaCloudStsWithCache = fetchAlibabaCloudStsWithCache; + exports.fetchAlibabaCloudSts = fetchAlibabaCloudSts; + exports.fetchAlibabaCloudStsAuto = fetchAlibabaCloudStsAuto; +} diff --git a/components/component-local-encryption.js b/components/component-local-encryption.js new file mode 100644 index 0000000..1a99dfa --- /dev/null +++ b/components/component-local-encryption.js @@ -0,0 +1,46 @@ +var __SEED1 = __.bytes.fromBase64('tdYcsSYN6tkKAEylW0TBfqiiKwea6AA/WMEyyfnRYacR3+DmflIpupFWbVovSfXvculFc7XUjV71jkID1+JJKg=='); +var __SEED2 = __readSeedFromFile(1024, '~/.jssp/.local-encryption-seed'); +var __SEED3 = __readSeedFromFile(2048, '~/.jssp-local-encryption-seed'); + +function __readSeedFromFile(len, filename) { + var seed; + var seedFile = $$.rFile(filename); + if (seedFile.exists()) { + seed = seedFile.string().trim(); + } else { + seed = $$.random().nextIoBytes(len).asBase64(); + seedFile.write(seed); + } + return seed; +} + +var __SEED = __SEED1 + '|' + __SEED2 + '|' + __SEED3; +var Bytes = Packages.me.hatter.tools.commons.bytes.Bytes; +var AESCryptTool = Packages.me.hatter.tools.commons.security.crypt.AESCryptTool; + +function encrypt(content) { + var key = $$.digests().sha256().digest(Bytes.from(__SEED).bytes()); + var nonce = $$.random().nextIoBytes(12); + + var cipher = AESCryptTool.gcmEncrypt(key.bytes(), nonce.bytes()).from(Bytes.from(content)).toBytes(); + return 'LOCALENC-V1.' + nonce.asBase64URI() + '.' + cipher.asBase64URI(); +} + +function decrypt(localEncCiphertext) { + localEncCiphertext = $STR(localEncCiphertext); + if (localEncCiphertext.indexOf('LOCALENC-V1.') != 0) { + throw 'Invalid local encryption ciphertext: ' + localEncCiphertext; + } + var key = $$.digests().sha256().digest(Bytes.from(__SEED).bytes()); + + var ciphertextParts = localEncCiphertext.split('\.'); + var nonce = Bytes.fromBase64URI(ciphertextParts[1]); + var ciphertext = Bytes.fromBase64URI(ciphertextParts[2]); + var plaintext = AESCryptTool.gcmDecrypt(key.bytes(), nonce.bytes()).from(ciphertext).toBytes(); + return $STR(plaintext.string()); +} + +if (typeof exports == 'object') { + exports.encrypt = encrypt; + exports.decrypt = decrypt; +}