@go-to-k/cdkd 0.117.2 → 0.119.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/cli.js +203 -53
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -37297,8 +37297,11 @@ function buildSubstitutionContextFromImageContext(context) {
|
|
|
37297
37297
|
* `physicalId`, and `Fn::GetAtt: [<Repo>, 'RepositoryUri']` shapes
|
|
37298
37298
|
* are resolved via the same state record.
|
|
37299
37299
|
*
|
|
37300
|
-
*
|
|
37301
|
-
*
|
|
37300
|
+
* Cross-account / cross-region pull (#455): `pullEcrImage` auto-detects
|
|
37301
|
+
* cross-account from `sts:GetCallerIdentity` and authenticates against
|
|
37302
|
+
* the URI's region directly. Pass `--ecr-role-arn <arn>` when the caller
|
|
37303
|
+
* does not already have cross-account `ecr:GetAuthorizationToken` /
|
|
37304
|
+
* `ecr:BatchGetImage` access on the target repository.
|
|
37302
37305
|
*/
|
|
37303
37306
|
function parseContainerImage(raw, containerName, taskLogicalId, resources, _stack, context) {
|
|
37304
37307
|
const getAttImage = tryResolveImageGetAtt(raw, resources, context);
|
|
@@ -37969,16 +37972,26 @@ function redactAwsCredentialsInArgs(args) {
|
|
|
37969
37972
|
//#endregion
|
|
37970
37973
|
//#region src/local/ecr-puller.ts
|
|
37971
37974
|
/**
|
|
37972
|
-
* ECR pull fallback for `cdkd local invoke`
|
|
37973
|
-
*
|
|
37975
|
+
* ECR pull fallback for `cdkd local invoke` / `cdkd local start-api` /
|
|
37976
|
+
* `cdkd local run-task`. When the image URI resolves to an ECR repo but
|
|
37974
37977
|
* doesn't match any cdk.out asset (typical when invoking a stack
|
|
37975
|
-
* deployed elsewhere
|
|
37976
|
-
*
|
|
37977
|
-
*
|
|
37978
|
-
* **
|
|
37979
|
-
* -
|
|
37980
|
-
*
|
|
37981
|
-
* -
|
|
37978
|
+
* deployed elsewhere or sharing a centralized registry), cdkd
|
|
37979
|
+
* authenticates against the target registry and runs `docker pull`.
|
|
37980
|
+
*
|
|
37981
|
+
* **Cross-account / cross-region** (#455):
|
|
37982
|
+
* - Same-account, same-region: fast path. No STS hop. The default
|
|
37983
|
+
* credential chain is used directly for `ecr:GetAuthorizationToken`.
|
|
37984
|
+
* - `ecrRoleArn` is provided: `sts:AssumeRole` is issued via the
|
|
37985
|
+
* default credential chain to obtain temporary credentials for the
|
|
37986
|
+
* target account. The resulting credentials authenticate the ECR
|
|
37987
|
+
* client (regardless of region — the ECR client is built for the
|
|
37988
|
+
* URI's region, which can differ from the caller's profile region).
|
|
37989
|
+
* - Cross-account, NO `ecrRoleArn`: cdkd falls through to the
|
|
37990
|
+
* default credential chain. This works when the caller has been
|
|
37991
|
+
* granted cross-account `ecr:GetAuthorizationToken` +
|
|
37992
|
+
* `ecr:BatchGetImage` permissions on the target repository via an
|
|
37993
|
+
* IAM policy; otherwise AWS rejects the call with `AccessDenied`
|
|
37994
|
+
* and the user is pointed at `--ecr-role-arn`.
|
|
37982
37995
|
*
|
|
37983
37996
|
* The `--no-pull` semantics (C3 in the design doc):
|
|
37984
37997
|
* - When NOT set: `ecrLogin` + `docker pull <uri>`.
|
|
@@ -38005,34 +38018,87 @@ function parseEcrUri(imageUri) {
|
|
|
38005
38018
|
};
|
|
38006
38019
|
}
|
|
38007
38020
|
/**
|
|
38008
|
-
*
|
|
38021
|
+
* Module-level cache for STS-issued AssumeRole credentials, keyed by
|
|
38022
|
+
* `(ecrRoleArn, callerRegion)`. Closes the reviewer's MAJOR finding: ECS
|
|
38023
|
+
* run-task with N containers under one `--ecr-role-arn` would otherwise issue
|
|
38024
|
+
* N× `AssumeRole` and N× `GetCallerIdentity` for identical credentials valid
|
|
38025
|
+
* for 3600s. The cache keeps a 5-minute safety margin against the recorded
|
|
38026
|
+
* `Expiration` so STS-side / local-clock skew never lets a stale entry through.
|
|
38027
|
+
*
|
|
38028
|
+
* Cache key is intentionally `(roleArn, region)` rather than full caller
|
|
38029
|
+
* identity — STS issues per-region session creds, and a switch of `--region`
|
|
38030
|
+
* between two `local invoke` calls in the same process must re-issue.
|
|
38031
|
+
*
|
|
38032
|
+
* NOT cleared on process exit — Node's module scope evaporates with the
|
|
38033
|
+
* process, and no inter-process sharing is desired (each `cdkd local invoke`
|
|
38034
|
+
* is its own isolated runtime).
|
|
38035
|
+
*/
|
|
38036
|
+
const ASSUMED_ROLE_CACHE = /* @__PURE__ */ new Map();
|
|
38037
|
+
/**
|
|
38038
|
+
* Module-level cache for `STS:GetCallerIdentity`. The result is identity-only
|
|
38039
|
+
* (`Account`) and invariant for the lifetime of the process under one set of
|
|
38040
|
+
* default credentials. Keyed by `callerRegion` to avoid a cross-region leak
|
|
38041
|
+
* when the caller flips `AWS_REGION` mid-process (STS is global but the SDK
|
|
38042
|
+
* uses regional endpoints; the result is invariant in practice, but we key
|
|
38043
|
+
* on region for safety).
|
|
38044
|
+
*/
|
|
38045
|
+
const CALLER_IDENTITY_CACHE = /* @__PURE__ */ new Map();
|
|
38046
|
+
/** 5-minute safety margin against the recorded STS expiration timestamp. */
|
|
38047
|
+
const STS_CREDENTIAL_SAFETY_MARGIN_MS = 300 * 1e3;
|
|
38048
|
+
function isCredentialFresh(creds) {
|
|
38049
|
+
if (!creds.expiration) return false;
|
|
38050
|
+
return creds.expiration.getTime() - Date.now() > STS_CREDENTIAL_SAFETY_MARGIN_MS;
|
|
38051
|
+
}
|
|
38052
|
+
/**
|
|
38053
|
+
* Pull (or verify locally cached) a container image from ECR.
|
|
38009
38054
|
*
|
|
38010
|
-
*
|
|
38011
|
-
*
|
|
38012
|
-
*
|
|
38055
|
+
* Auto-detects cross-account from `STS:GetCallerIdentity` and assumes
|
|
38056
|
+
* the supplied role when set. Returns the image URI the caller should
|
|
38057
|
+
* pass to `docker run` (same as the input — no rewriting).
|
|
38013
38058
|
*/
|
|
38014
38059
|
async function pullEcrImage(imageUri, options) {
|
|
38015
38060
|
const logger = getLogger().child("ecr-puller");
|
|
38016
38061
|
const parsed = parseEcrUri(imageUri);
|
|
38017
38062
|
if (!parsed) throw new LocalInvokeBuildError(`Image URI '${imageUri}' is not an ECR URI. cdkd local invoke v1 only authenticates against ECR for the deployed-image fallback path.`);
|
|
38018
|
-
const sts = new STSClient({ region: parsed.region });
|
|
38019
|
-
let callerAccount;
|
|
38020
|
-
try {
|
|
38021
|
-
const identity = await sts.send(new GetCallerIdentityCommand({}));
|
|
38022
|
-
if (!identity.Account) throw new LocalInvokeBuildError("STS GetCallerIdentity returned no Account. Verify your AWS credentials.");
|
|
38023
|
-
callerAccount = identity.Account;
|
|
38024
|
-
} finally {
|
|
38025
|
-
sts.destroy();
|
|
38026
|
-
}
|
|
38027
|
-
if (callerAccount !== parsed.accountId) throw new LocalInvokeBuildError(`Image URI '${imageUri}' is in account ${parsed.accountId}, but the caller is ${callerAccount}. Cross-account ECR pull is not supported in cdkd local invoke v1 — deferred to a follow-up PR. Workaround: assume a role in the target account before invoking, or build the image locally with \`cdkd local invoke -a cdk.out\` (no ECR pull).`);
|
|
38028
38063
|
const callerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"];
|
|
38029
|
-
if (callerRegion && callerRegion !== parsed.region) throw new LocalInvokeBuildError(`Image URI '${imageUri}' is in region ${parsed.region}, but the caller's region is ${callerRegion}. Cross-region ECR pull is not supported in cdkd local invoke v1 — deferred to a follow-up PR. Workaround: re-run with AWS_REGION=${parsed.region} set, or build the image locally with -a cdk.out.`);
|
|
38030
38064
|
if (options.skipPull) {
|
|
38031
38065
|
logger.info(`Skipping ECR pull (--no-pull). Verifying ${imageUri} is in local cache...`);
|
|
38032
38066
|
await verifyImageInLocalCache(imageUri);
|
|
38033
38067
|
return imageUri;
|
|
38034
38068
|
}
|
|
38035
|
-
const
|
|
38069
|
+
const callerIdentityKey = callerRegion ?? "_unset";
|
|
38070
|
+
let callerAccount = CALLER_IDENTITY_CACHE.get(callerIdentityKey);
|
|
38071
|
+
if (callerAccount === void 0) {
|
|
38072
|
+
const sts = new STSClient({ ...callerRegion && { region: callerRegion } });
|
|
38073
|
+
try {
|
|
38074
|
+
const identity = await sts.send(new GetCallerIdentityCommand({}));
|
|
38075
|
+
if (!identity.Account) throw new LocalInvokeBuildError("STS GetCallerIdentity returned no Account. Verify your AWS credentials.");
|
|
38076
|
+
callerAccount = identity.Account;
|
|
38077
|
+
CALLER_IDENTITY_CACHE.set(callerIdentityKey, callerAccount);
|
|
38078
|
+
} finally {
|
|
38079
|
+
sts.destroy();
|
|
38080
|
+
}
|
|
38081
|
+
}
|
|
38082
|
+
const crossAccount = callerAccount !== parsed.accountId;
|
|
38083
|
+
const crossRegion = callerRegion !== void 0 && callerRegion !== parsed.region;
|
|
38084
|
+
let assumed;
|
|
38085
|
+
if (options.ecrRoleArn) {
|
|
38086
|
+
const cacheKey = `${options.ecrRoleArn}|${callerRegion ?? "_unset"}`;
|
|
38087
|
+
const cached = ASSUMED_ROLE_CACHE.get(cacheKey);
|
|
38088
|
+
if (cached && isCredentialFresh(cached)) {
|
|
38089
|
+
assumed = cached;
|
|
38090
|
+
logger.debug(`Reusing cached AssumeRole credentials for ${options.ecrRoleArn}`);
|
|
38091
|
+
} else {
|
|
38092
|
+
assumed = await assumeRoleForEcr(options.ecrRoleArn, callerRegion, logger);
|
|
38093
|
+
ASSUMED_ROLE_CACHE.set(cacheKey, assumed);
|
|
38094
|
+
logger.info(`Assumed role ${options.ecrRoleArn} for ECR pull (account=${parsed.accountId}, region=${parsed.region})`);
|
|
38095
|
+
}
|
|
38096
|
+
} else if (crossAccount) logger.info(`Cross-account ECR pull: image account ${parsed.accountId} != caller ${callerAccount}. Using the caller's credentials; pass --ecr-role-arn <arn> if AWS rejects with AccessDenied.`);
|
|
38097
|
+
if (crossRegion) logger.info(`Cross-region ECR pull: image region ${parsed.region} != caller ${callerRegion ?? "(unset)"}. Authenticating against the image region directly.`);
|
|
38098
|
+
const ecr = new ECRClient({
|
|
38099
|
+
region: parsed.region,
|
|
38100
|
+
...assumed && { credentials: assumed }
|
|
38101
|
+
});
|
|
38036
38102
|
try {
|
|
38037
38103
|
await ecrLogin(ecr, parsed.accountId, parsed.region);
|
|
38038
38104
|
} finally {
|
|
@@ -38047,10 +38113,39 @@ async function pullEcrImage(imageUri, options) {
|
|
|
38047
38113
|
return imageUri;
|
|
38048
38114
|
}
|
|
38049
38115
|
/**
|
|
38050
|
-
*
|
|
38051
|
-
*
|
|
38052
|
-
*
|
|
38053
|
-
*
|
|
38116
|
+
* Assume the supplied role via the SDK default credential chain and
|
|
38117
|
+
* return the resulting temporary credentials. The STS client is built
|
|
38118
|
+
* with the caller's profile region (or unset) — STS is a global
|
|
38119
|
+
* service so the region is informational, but threading it through
|
|
38120
|
+
* mirrors the convention used by `src/utils/role-arn.ts`.
|
|
38121
|
+
*/
|
|
38122
|
+
async function assumeRoleForEcr(roleArn, callerRegion, logger) {
|
|
38123
|
+
logger.debug(`Assuming role ${roleArn} for ECR pull...`);
|
|
38124
|
+
const sts = new STSClient({ ...callerRegion && { region: callerRegion } });
|
|
38125
|
+
try {
|
|
38126
|
+
const creds = (await sts.send(new AssumeRoleCommand({
|
|
38127
|
+
RoleArn: roleArn,
|
|
38128
|
+
RoleSessionName: `cdkd-local-ecr-${Date.now()}`,
|
|
38129
|
+
DurationSeconds: 3600
|
|
38130
|
+
}))).Credentials;
|
|
38131
|
+
if (!creds || !creds.AccessKeyId || !creds.SecretAccessKey || !creds.SessionToken) throw new LocalInvokeBuildError(`AssumeRole(${roleArn}) returned no usable credentials. Verify the role's trust policy allows your identity to assume it.`);
|
|
38132
|
+
return {
|
|
38133
|
+
accessKeyId: creds.AccessKeyId,
|
|
38134
|
+
secretAccessKey: creds.SecretAccessKey,
|
|
38135
|
+
sessionToken: creds.SessionToken,
|
|
38136
|
+
...creds.Expiration && { expiration: creds.Expiration }
|
|
38137
|
+
};
|
|
38138
|
+
} catch (err) {
|
|
38139
|
+
if (err instanceof LocalInvokeBuildError) throw err;
|
|
38140
|
+
throw new LocalInvokeBuildError(`Failed to assume role ${roleArn} for ECR pull: ${err instanceof Error ? err.message : String(err)}. Verify the role exists and its trust policy permits the caller's identity to assume it.`);
|
|
38141
|
+
} finally {
|
|
38142
|
+
sts.destroy();
|
|
38143
|
+
}
|
|
38144
|
+
}
|
|
38145
|
+
/**
|
|
38146
|
+
* Authenticate the local docker daemon against the target ECR registry.
|
|
38147
|
+
* Mirrors `DockerAssetPublisher.ecrLogin` but stays in this module so the
|
|
38148
|
+
* local-invoke path doesn't depend on the publisher's larger surface area.
|
|
38054
38149
|
*/
|
|
38055
38150
|
async function ecrLogin(client, accountId, region) {
|
|
38056
38151
|
getLogger().child("ecr-puller").debug(`ECR login (account=${accountId}, region=${region})`);
|
|
@@ -40353,14 +40448,23 @@ function resolveRestV1Authorizer(authorizerLogicalId, template, stackName, decla
|
|
|
40353
40448
|
if (type === "COGNITO_USER_POOLS") {
|
|
40354
40449
|
const arns = props["ProviderARNs"];
|
|
40355
40450
|
if (!Array.isArray(arns) || arns.length === 0) throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: COGNITO_USER_POOLS authorizer is missing ProviderARNs.`);
|
|
40356
|
-
const
|
|
40357
|
-
|
|
40451
|
+
const pools = arns.map((entry, idx) => {
|
|
40452
|
+
const arn = pickStringFromArn(entry, `${stackName}/${authorizerLogicalId}.ProviderARNs[${idx}]`);
|
|
40453
|
+
const parsed = parseCognitoUserPoolArn(arn, `${stackName}/${authorizerLogicalId}.ProviderARNs[${idx}]`);
|
|
40454
|
+
return {
|
|
40455
|
+
userPoolArn: arn,
|
|
40456
|
+
region: parsed.region,
|
|
40457
|
+
userPoolId: parsed.userPoolId
|
|
40458
|
+
};
|
|
40459
|
+
});
|
|
40460
|
+
const first = pools[0];
|
|
40358
40461
|
return {
|
|
40359
40462
|
kind: "cognito",
|
|
40360
40463
|
logicalId: authorizerLogicalId,
|
|
40361
|
-
|
|
40362
|
-
|
|
40363
|
-
|
|
40464
|
+
pools,
|
|
40465
|
+
userPoolArn: first.userPoolArn,
|
|
40466
|
+
region: first.region,
|
|
40467
|
+
userPoolId: first.userPoolId,
|
|
40364
40468
|
declaredAt
|
|
40365
40469
|
};
|
|
40366
40470
|
}
|
|
@@ -40582,7 +40686,9 @@ function parseCognitoIssuer(issuer) {
|
|
|
40582
40686
|
/**
|
|
40583
40687
|
* Pull a string out of a {Ref} / literal entry under `ProviderARNs`.
|
|
40584
40688
|
* CDK's CognitoUserPoolsAuthorizer emits a literal array of `Fn::GetAtt:
|
|
40585
|
-
* [<UserPool>, 'Arn']` entries — we accept both.
|
|
40689
|
+
* [<UserPool>, 'Arn']` entries — we accept both. The `location` argument
|
|
40690
|
+
* carries the full `<stack>/<authorizer>.ProviderARNs[<idx>]` path so the
|
|
40691
|
+
* error names the offending entry exactly.
|
|
40586
40692
|
*/
|
|
40587
40693
|
function pickStringFromArn(value, location) {
|
|
40588
40694
|
if (typeof value === "string") return value;
|
|
@@ -40590,10 +40696,10 @@ function pickStringFromArn(value, location) {
|
|
|
40590
40696
|
const obj = value;
|
|
40591
40697
|
if ("Fn::GetAtt" in obj) {
|
|
40592
40698
|
const arg = obj["Fn::GetAtt"];
|
|
40593
|
-
if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] === "Arn") throw new RouteDiscoveryError(`${location}:
|
|
40699
|
+
if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] === "Arn") throw new RouteDiscoveryError(`${location}: uses Fn::GetAtt against logical ID '${arg[0]}'. cdkd local start-api needs the literal ARN string to derive the JWKS URL — set the user pool ARN explicitly via 'authorizer.providerArns' on the CDK construct, or upgrade to JWT (HTTP v2) which encodes the pool in the Issuer URL.`);
|
|
40594
40700
|
}
|
|
40595
40701
|
}
|
|
40596
|
-
throw new RouteDiscoveryError(`${location}:
|
|
40702
|
+
throw new RouteDiscoveryError(`${location}: must be a literal string (got ${shortJson(value)}).`);
|
|
40597
40703
|
}
|
|
40598
40704
|
/**
|
|
40599
40705
|
* Parse and clamp a TTL value to `[0, max]` with a default fallback.
|
|
@@ -41111,21 +41217,36 @@ function buildJwksUrlFromIssuer(issuer) {
|
|
|
41111
41217
|
return `${issuer.replace(/\/+$/, "")}/.well-known/jwks.json`;
|
|
41112
41218
|
}
|
|
41113
41219
|
/**
|
|
41114
|
-
*
|
|
41115
|
-
*
|
|
41116
|
-
|
|
41117
|
-
|
|
41220
|
+
* Build the expected `iss` claim URL for a Cognito user pool. Matches the
|
|
41221
|
+
* issuer Cognito embeds in every minted JWT.
|
|
41222
|
+
*/
|
|
41223
|
+
function buildCognitoIssuer(region, userPoolId) {
|
|
41224
|
+
return `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`;
|
|
41225
|
+
}
|
|
41226
|
+
/**
|
|
41227
|
+
* Verify a Bearer JWT against the Cognito user pool(s) referenced by the
|
|
41228
|
+
* authorizer. With a multi-pool authorizer (`ProviderARNs[]` of length
|
|
41229
|
+
* 1+) the request-time pool selection is driven by the JWT's unverified
|
|
41230
|
+
* `iss` claim — only the matching pool's JWKS is used for signature
|
|
41231
|
+
* verification, so a token issued by pool A cannot be verified against
|
|
41232
|
+
* pool B's keys. Issuer mismatch against EVERY configured pool rejects
|
|
41233
|
+
* with 401.
|
|
41118
41234
|
*
|
|
41119
41235
|
* Returns `{ allow: false }` on:
|
|
41120
41236
|
* - missing / malformed Authorization header (caller surfaces 401);
|
|
41121
41237
|
* - signature verification failure;
|
|
41122
41238
|
* - expired token (`exp` in the past);
|
|
41123
|
-
* - issuer mismatch (token's `iss` doesn't match
|
|
41239
|
+
* - issuer mismatch (token's `iss` doesn't match any configured pool);
|
|
41124
41240
|
* - audience mismatch (token's `aud` not in the configured allowlist).
|
|
41125
41241
|
*
|
|
41126
41242
|
* Returns `{ allow: true, principalId, context }` on:
|
|
41127
|
-
* - successful verification;
|
|
41128
|
-
* - JWKS-unreachable pass-through mode
|
|
41243
|
+
* - successful verification against the matching pool;
|
|
41244
|
+
* - JWKS-unreachable pass-through mode for the matching pool (with a
|
|
41245
|
+
* warn line on first hit; per-pool TTL handled by the cache).
|
|
41246
|
+
*
|
|
41247
|
+
* Backward compat: a single-element `ProviderARNs[]` (the historical
|
|
41248
|
+
* single-pool case) behaves identically to pre-PR — `pools[0]` is the
|
|
41249
|
+
* only candidate and the `iss` check matches it.
|
|
41129
41250
|
*/
|
|
41130
41251
|
async function verifyCognitoJwt(authorizer, authorizationHeader, jwksCache, opts = {}) {
|
|
41131
41252
|
const now = opts.now ?? (() => Date.now());
|
|
@@ -41135,7 +41256,33 @@ async function verifyCognitoJwt(authorizer, authorizationHeader, jwksCache, opts
|
|
|
41135
41256
|
identityHash: void 0,
|
|
41136
41257
|
ttlSeconds: 0
|
|
41137
41258
|
};
|
|
41138
|
-
|
|
41259
|
+
const pools = authorizer.pools && authorizer.pools.length > 0 ? authorizer.pools : [{
|
|
41260
|
+
userPoolArn: authorizer.userPoolArn,
|
|
41261
|
+
region: authorizer.region,
|
|
41262
|
+
userPoolId: authorizer.userPoolId
|
|
41263
|
+
}];
|
|
41264
|
+
const parsed = parseJwt(token);
|
|
41265
|
+
const identityHash = buildIdentityHash([token]);
|
|
41266
|
+
let selectedPool = pools[0];
|
|
41267
|
+
let issMatched = false;
|
|
41268
|
+
if (parsed && typeof parsed.payload["iss"] === "string") {
|
|
41269
|
+
const tokenIss = parsed.payload["iss"].replace(/\/+$/, "");
|
|
41270
|
+
for (const pool of pools) if (buildCognitoIssuer(pool.region, pool.userPoolId) === tokenIss) {
|
|
41271
|
+
selectedPool = pool;
|
|
41272
|
+
issMatched = true;
|
|
41273
|
+
break;
|
|
41274
|
+
}
|
|
41275
|
+
if (!issMatched) return {
|
|
41276
|
+
allow: false,
|
|
41277
|
+
identityHash,
|
|
41278
|
+
ttlSeconds: 0
|
|
41279
|
+
};
|
|
41280
|
+
} else if (pools.length > 1) return {
|
|
41281
|
+
allow: false,
|
|
41282
|
+
identityHash,
|
|
41283
|
+
ttlSeconds: 0
|
|
41284
|
+
};
|
|
41285
|
+
return verifyAndShape(token, buildCognitoJwksUrl(selectedPool.region, selectedPool.userPoolId), buildCognitoIssuer(selectedPool.region, selectedPool.userPoolId), void 0, jwksCache, opts.warned, now);
|
|
41139
41286
|
}
|
|
41140
41287
|
/**
|
|
41141
41288
|
* Verify a Bearer JWT against an HTTP v2 JWT authorizer's `JwtConfiguration`.
|
|
@@ -42617,7 +42764,7 @@ async function prewarmJwks(routesWithAuth, jwksCache) {
|
|
|
42617
42764
|
for (const entry of routesWithAuth) {
|
|
42618
42765
|
const auth = entry.authorizer;
|
|
42619
42766
|
if (!auth) continue;
|
|
42620
|
-
if (auth.kind === "cognito") urls.add(buildCognitoJwksUrl(
|
|
42767
|
+
if (auth.kind === "cognito") for (const pool of auth.pools) urls.add(buildCognitoJwksUrl(pool.region, pool.userPoolId));
|
|
42621
42768
|
else if (auth.kind === "jwt") {
|
|
42622
42769
|
const url = auth.region && auth.userPoolId ? buildCognitoJwksUrl(auth.region, auth.userPoolId) : buildJwksUrlFromIssuer(auth.issuer);
|
|
42623
42770
|
urls.add(url);
|
|
@@ -43745,7 +43892,8 @@ async function prepareOneImage(task, container, options) {
|
|
|
43745
43892
|
return image.uri;
|
|
43746
43893
|
case "ecr": return pullEcrImage(image.uri, {
|
|
43747
43894
|
skipPull: options.skipPull,
|
|
43748
|
-
...options.region !== void 0 && { region: options.region }
|
|
43895
|
+
...options.region !== void 0 && { region: options.region },
|
|
43896
|
+
...options.ecrRoleArn !== void 0 && { ecrRoleArn: options.ecrRoleArn }
|
|
43749
43897
|
});
|
|
43750
43898
|
case "cdk-asset": {
|
|
43751
43899
|
const cdkOutDir = task.stack.assetManifestPath ? dirname(task.stack.assetManifestPath) : void 0;
|
|
@@ -43999,6 +44147,7 @@ async function localRunTaskCommand(target, options) {
|
|
|
43999
44147
|
if (resolvedRoleArn) runOpts.taskRoleArn = resolvedRoleArn;
|
|
44000
44148
|
if (options.platform) runOpts.platformOverride = options.platform;
|
|
44001
44149
|
if (options.region) runOpts.region = options.region;
|
|
44150
|
+
if (options.ecrRoleArn) runOpts.ecrRoleArn = options.ecrRoleArn;
|
|
44002
44151
|
const result = await runEcsTask(task, runOpts, state);
|
|
44003
44152
|
if (options.detach) {
|
|
44004
44153
|
logger.info("Task containers started in detached mode; cdkd is exiting.");
|
|
@@ -44148,7 +44297,7 @@ function readEnvOverridesFile$1(filePath) {
|
|
|
44148
44297
|
return parsed;
|
|
44149
44298
|
}
|
|
44150
44299
|
function createLocalRunTaskCommand() {
|
|
44151
|
-
const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localRunTaskCommand));
|
|
44300
|
+
const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localRunTaskCommand));
|
|
44152
44301
|
[
|
|
44153
44302
|
...commonOptions,
|
|
44154
44303
|
...appOptions,
|
|
@@ -44492,7 +44641,8 @@ async function resolveContainerImagePlan(lambda, options) {
|
|
|
44492
44641
|
logger.info(`No matching cdk.out asset for ${lambda.imageUri}; falling back to ECR pull (same-acct/region only)...`);
|
|
44493
44642
|
imageRef = await pullEcrImage(lambda.imageUri, {
|
|
44494
44643
|
skipPull: options.pull === false,
|
|
44495
|
-
...options.region !== void 0 && { region: options.region }
|
|
44644
|
+
...options.region !== void 0 && { region: options.region },
|
|
44645
|
+
...options.ecrRoleArn !== void 0 && { ecrRoleArn: options.ecrRoleArn }
|
|
44496
44646
|
});
|
|
44497
44647
|
}
|
|
44498
44648
|
const tmpfs = resolveTmpfsForLambda(lambda);
|
|
@@ -44786,7 +44936,7 @@ function pickReferencedLogicalId(intrinsic) {
|
|
|
44786
44936
|
*/
|
|
44787
44937
|
function createLocalCommand() {
|
|
44788
44938
|
const local = new Command("local").description("Local execution of Lambda functions (RIE) and ECS task definitions (Docker required)");
|
|
44789
|
-
const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localInvokeCommand));
|
|
44939
|
+
const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localInvokeCommand));
|
|
44790
44940
|
[
|
|
44791
44941
|
...commonOptions,
|
|
44792
44942
|
...appOptions,
|
|
@@ -46074,7 +46224,7 @@ function reorderArgs(argv) {
|
|
|
46074
46224
|
*/
|
|
46075
46225
|
async function main() {
|
|
46076
46226
|
const program = new Command();
|
|
46077
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
46227
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.119.0");
|
|
46078
46228
|
program.addCommand(createBootstrapCommand());
|
|
46079
46229
|
program.addCommand(createSynthCommand());
|
|
46080
46230
|
program.addCommand(createListCommand());
|