[AWS CDK] EventBridge Scheduler の Universal Target で IAM ポリシーにワイルドカード (*) が自動付与される問題の解決方法
こんにちは、製造ビジネステクノロジー部の若槻です。
Amazon EventBridge Scheduler では Universal Targets を使用することで、Lambda や Step Functions、ECS タスクなど様々な AWS サービスをターゲットにスケジュール実行できます。
Universal Targets は AWS CDK の L2 コンストラクトとしても提供されており、以下のドキュメントで使用方法が紹介されています。
今回は、この EventBridge Scheduler の Universal Target を CDK で使用する際に、IAM ポリシーにワイルドカード (*) が自動付与される問題の解決方法 について紹介します。
問題
次のように Lambda の Provisioned Concurrency を有効化する EventBridge Scheduler を作成するコンストラクトを実装しました。
Scheduler の Universal Target に設定する IAM ロールは明示的に作成し、最小権限の権限ポリシーとしています。
// Warning 発生: 未解決
// ワイルドポリシー自動付与: 未解決
import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as scheduler from "aws-cdk-lib/aws-scheduler";
import * as scheduler_targets from "aws-cdk-lib/aws-scheduler-targets";
import { Construct } from "constructs";
interface EnablePcSchedulerProps {
alias: lambda.Alias;
}
/**
* Lambda の Provisioned Concurrency を有効化する Scheduler を作成する Construct
*/
export class EnablePcScheduler extends Construct {
constructor(scope: Construct, id: string, props: EnablePcSchedulerProps) {
super(scope, id);
const { alias } = props;
/**
* 最小権限の IAM Role の作成
*/
const role = new iam.Role(this, "Role", {
assumedBy: new iam.ServicePrincipal("scheduler.amazonaws.com"),
});
role.addToPolicy(
new iam.PolicyStatement({
actions: ["lambda:PutProvisionedConcurrencyConfig"],
resources: [alias.functionArn],
})
);
/**
* Scheduler Target の作成
*/
const target = new scheduler_targets.Universal({
service: "lambda",
action: "putProvisionedConcurrencyConfig",
role, // 作成した IAM Role を明示的に指定
input: scheduler.ScheduleTargetInput.fromObject({
FunctionName: alias.functionName,
Qualifier: alias.aliasName,
ProvisionedConcurrentExecutions: 5,
}),
});
/**
* 平日9時に Provisioned Concurrency を有効化する Scheduler の作成
*/
new scheduler.Schedule(this, "Default", {
schedule: scheduler.ScheduleExpression.cron({
minute: "0",
hour: "9-18",
weekDay: "MON-FRI",
timeZone: cdk.TimeZone.ASIA_TOKYO,
}),
target,
});
}
}
しかし上記をデプロイするとコマンド実行結果に以下の Warning が表示されました。
[Warning at /Sample/EnablePcScheduler/Role] Default policy with * for resources is used. Use custom policy for better security posture. [ack: @aws-cdk/aws-scheduler-targets:defaultWildcardResourcePolicy]
作成された IAM Policy を見ると、Resource 句に明示的に指定した ARN に加え、ワイルドカード (*)が追加されていました。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "lambda:PutProvisionedConcurrencyConfig",
"Resource": [
"*",
"arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:Sample-SampleFunction7DB1D36A-KZtPV8JOPRdt:Prod"
],
"Effect": "Allow"
}
]
}
警告の通り、L2 コンストラクトで内部的にデフォルトでワイルドカードの権限自動付与が行われているようですが、セキュリティを考慮すると望ましくはありませんね。
解決
次のように、policyStatements プロパティで明示的にポリシーステートメントを指定することで、ワイルドカードの権限自動付与および警告の発生を抑制できました。
import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as scheduler from "aws-cdk-lib/aws-scheduler";
import * as scheduler_targets from "aws-cdk-lib/aws-scheduler-targets";
import { Construct } from "constructs";
interface EnablePcSchedulerProps {
alias: lambda.Alias;
}
/**
* Lambda の Provisioned Concurrency を有効化するスケジューラーを作成する Construct
*/
export class EnablePcScheduler extends Construct {
constructor(scope: Construct, id: string, props: EnablePcSchedulerProps) {
super(scope, id);
const { alias } = props;
/**
* Scheduler Target の作成
*/
const target = new scheduler_targets.Universal({
service: "lambda",
action: "putProvisionedConcurrencyConfig",
/**
* 権限自動付与を回避するため、policyStatements で明示的に権限を指定する。
* @see https://github.com/aws/aws-cdk/blob/068c9f90160e21503eca091c9db4b339f669e2dd/packages/aws-cdk-lib/aws-scheduler-targets/lib/universal.ts#L96
*/
policyStatements: [
new iam.PolicyStatement({
actions: ["lambda:PutProvisionedConcurrencyConfig"],
resources: [alias.functionArn],
}),
],
input: scheduler.ScheduleTargetInput.fromObject({
FunctionName: alias.functionName,
Qualifier: alias.aliasName,
ProvisionedConcurrentExecutions: 5,
}),
});
/**
* 平日9時に Provisioned Concurrency を有効化するスケジューラー
*/
new scheduler.Schedule(this, "Default", {
schedule: scheduler.ScheduleExpression.cron({
minute: "0",
hour: "9-18",
weekDay: "MON-FRI",
timeZone: cdk.TimeZone.ASIA_TOKYO,
}),
target,
});
}
}
上記をデプロイすると、作成された IAM ポリシーは以下の通りとなり、ワイルドカードの権限自動付与および警告の発生も抑制されました。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "lambda:PutProvisionedConcurrencyConfig",
"Resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:Sample-SampleFunction7DB1D36A-KZtPV8JOPRdt:Prod",
"Effect": "Allow"
}
]
}
解説ですが、下記の Universal クラスのソースコードを確認すると、policyStatements を省略すると「*(全リソース)」の権限を自動的に追加し、警告もセットで出す仕様となっています。
よって、ワイルドカードを避けて最小権限で運用したい場合は、policyStatements を明示的に記述する必要があるのですね。
別解
当初、policyStatements プロパティを指定する方法に気づかず、別の方法でワイルドカードの権限自動付与および警告の発生を抑制できましたので、参考までに紹介します。
ちなみにいずれの方法も acknowledgeWarning() により明示的な警告抑制が必要となりました。
その1: withoutPolicyUpdates() による方法
次の2つの対応により、ワイルドカードの権限自動付与および警告の発生を抑制できました。
- 最小権限の IAM ロールを作成し、
role.withoutPolicyUpdates()を使用してターゲットに渡す Annotations.of(node).acknowledgeWarning()を使用して警告を抑制する
方法詳細
import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as scheduler from "aws-cdk-lib/aws-scheduler";
import * as scheduler_targets from "aws-cdk-lib/aws-scheduler-targets";
import { Construct } from "constructs";
interface EnablePcSchedulerProps {
alias: lambda.Alias;
}
/**
* Lambda の Provisioned Concurrency を有効化するスケジューラーを作成する Construct
*/
export class EnablePcScheduler extends Construct {
constructor(scope: Construct, id: string, props: EnablePcSchedulerProps) {
super(scope, id);
const { alias } = props;
/**
* 最小権限の IAM Role の作成
*/
const role = new iam.Role(this, "Role", {
assumedBy: new iam.ServicePrincipal("scheduler.amazonaws.com"),
});
role.addToPolicy(
new iam.PolicyStatement({
actions: ["lambda:PutProvisionedConcurrencyConfig"],
resources: [alias.functionArn],
})
);
/**
* Scheduler Target の作成
*/
const target = new scheduler_targets.Universal({
service: "lambda",
action: "putProvisionedConcurrencyConfig",
role: role.withoutPolicyUpdates(), // ポリシー自動更新を無効化してワイルドカード付与を防止
input: scheduler.ScheduleTargetInput.fromObject({
FunctionName: alias.functionName,
Qualifier: alias.aliasName,
ProvisionedConcurrentExecutions: 5,
}),
});
/**
* 平日9時に Provisioned Concurrency を有効化するスケジューラー
*/
new scheduler.Schedule(this, "Default", {
schedule: scheduler.ScheduleExpression.cron({
minute: "0",
hour: "9-18",
weekDay: "MON-FRI",
timeZone: cdk.TimeZone.ASIA_TOKYO,
}),
target,
});
/**
* Universal が内部的に生成するロールの警告を抑制
*/
cdk.Aspects.of(scope).add({
visit(node) {
cdk.Annotations.of(node).acknowledgeWarning(
"@aws-cdk/aws-scheduler-targets:defaultWildcardResourcePolicy"
);
},
});
}
}
上記をデプロイすると、作成された IAM ポリシーは以下の通りとなり、ワイルドカードの権限自動付与および警告の発生も抑制されました。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "lambda:PutProvisionedConcurrencyConfig",
"Resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:Sample-SampleFunction7DB1D36A-KZtPV8JOPRdt:Prod",
"Effect": "Allow"
}
]
}
withoutPolicyUpdates() を使用すると元の Role オブジェクトをポリシーが変更不能な ImmutableRole に変換でき、L2 コンストラクトによる権限自動付与を防止できます。下記で詳しく解説されています。
その2: エスケープハッチによる方法
エスケープハッチを使用して target ロールを role で置き換えることにより、権限自動付与を無効化することもできます。
方法詳細
import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as scheduler from "aws-cdk-lib/aws-scheduler";
import * as scheduler_targets from "aws-cdk-lib/aws-scheduler-targets";
import { Construct } from "constructs";
interface EnablePcSchedulerProps {
alias: lambda.Alias;
}
/**
* Lambda の Provisioned Concurrency を有効化するスケジューラーを作成する Construct
*/
export class EnablePcScheduler extends Construct {
constructor(scope: Construct, id: string, props: EnablePcSchedulerProps) {
super(scope, id);
const { alias } = props;
/**
* 最小権限の IAM Role の作成
*/
const role = new iam.Role(this, "Role", {
assumedBy: new iam.ServicePrincipal("scheduler.amazonaws.com"),
});
role.addToPolicy(
new iam.PolicyStatement({
actions: ["lambda:PutProvisionedConcurrencyConfig"],
resources: [alias.functionArn],
})
);
/**
* Scheduler Target の作成
*/
const target = new scheduler_targets.Universal({
service: "lambda",
action: "putProvisionedConcurrencyConfig",
input: scheduler.ScheduleTargetInput.fromObject({
FunctionName: alias.functionName,
Qualifier: alias.aliasName,
ProvisionedConcurrentExecutions: 5,
}),
});
/**
* エスケープハッチを使用して target ロールを role で置き換える
*/
(target as any).role = role;
/**
* 平日9時に Provisioned Concurrency を有効化するスケジューラー
*/
new scheduler.Schedule(this, "Default", {
schedule: scheduler.ScheduleExpression.cron({
minute: "0",
hour: "9-18",
weekDay: "MON-FRI",
timeZone: cdk.TimeZone.ASIA_TOKYO,
}),
target,
});
/**
* Universal が内部的に生成するロールの警告を抑制
*/
cdk.Aspects.of(scope).add({
visit(node) {
cdk.Annotations.of(node).acknowledgeWarning(
"@aws-cdk/aws-scheduler-targets:defaultWildcardResourcePolicy"
);
},
});
}
}
上記をデプロイすると、作成された IAM ポリシーは以下の通りとなり、ワイルドカードの権限自動付与および警告の発生も抑制されました。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "lambda:PutProvisionedConcurrencyConfig",
"Resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:Sample-SampleFunction7DB1D36A-KZtPV8JOPRdt:Prod",
"Effect": "Allow"
}
]
}
おわりに
Amazon EventBridge Scheduler の Universal Target を CDK で使用する際に、IAM ポリシーにワイルドカード (*) が自動付与される問題の解決方法について紹介しました。
ワイルドカードによる過剰な権限付与はセキュリティリスクとなります。CDK デプロイ時の警告を無視したり、無闇に抑制したりせずに、今回紹介したように policyStatements プロパティを使用して最小権限のポリシーステートメントを明示的に指定することをお勧めします。
以上







