Lambda関数でのVPCの関連付けについて

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。

今回は、AWS Lambda関数が VPC (Virtual Private Cloud) に関連付けられていない 状態について、そのリスクと対策を解説します。

ポリシーの説明

Lambda の Security Hub ブコントロール – AWS Security Hub

このコントロールは、Lambda 関数が 仮想プライベートクラウド (VPC) にデプロイされているかどうかをチェックします。Lambda 関数が VPC にデプロイされていない場合、コントロールは失敗します。Security Hub は、パブリック到達可能性を判断するための VPC サブネットルーティング設定は評価しません。Lambda@Edge リソースに関する失敗の結果が表示される場合があります。

リスク

AWS Lambda関数は、サーバーレスアプリケーションの基盤として広く利用されています。しかし、Lambda関数がVPCに関連付けられていない場合、特にVPC内のプライベートリソース(Amazon RDSデータベース、Amazon ElastiCache、Amazon Redshift、EC2インスタンスなど)にアクセスする必要がある場合に、以下のようなセキュリティおよび運用上のリスクが発生します。

  • VPC内プライベートリソースへのアクセス不可: VPCに関連付けられていないLambda関数は、デフォルトでAWSのパブリックネットワークから実行されます。このため、プライベートサブネット内のデータベースやサービスといったVPC内部のリソースに直接アクセスすることができません。もしアクセスが必要な場合は、NAT GatewayやVPCエンドポイントといった追加のネットワークコンポーネントを介して間接的にアクセスする必要があり、設定が複雑になるだけでなく、不要なパブリックアクセスポイントが発生する可能性があります。
  • ネットワークセキュリティ制御の欠如: VPCに関連付けられていないLambda関数には、セキュリティグループネットワークACL (NACL) を適用できません。これにより、Lambda関数からのアウトバウンドトラフィックやインバウンドトラフィックをきめ細かく制御することができず、セキュリティ体制が脆弱になります。例えば、特定のIPアドレス範囲へのアクセスのみを許可したり、特定のポートへのアクセスを制限したりすることができません。

VPC内のリソースにアクセスする場合においては必須になるため、必要性に応じて適用するようにしましょう。


対策

Lambda関数をVPCに関連付けることは、VPC内のプライベートリソースへのセキュアなアクセスを可能にし、ネットワークセキュリティ制御を強化するための重要なベストプラクティスです。

  • 適切なサブネットの選択: Lambda関数をデプロイするVPC内のプライベートサブネットを選択します。これにより、関数はインターネットに直接公開されず、VPCのネットワーク分離の恩恵を受けられます。複数のアベイラビリティゾーンにまたがるサブネットを選択することで、高可用性を確保します。
  • セキュリティグループの適用: Lambda関数がVPC内の他のリソースと通信するために必要なポートとプロトコルのみを許可するセキュリティグループを割り当てます。これにより、関数のネットワークトラフィックを厳密に制御できます。例えば、RDSへのアクセスにはポート5432 (PostgreSQL) や3306 (MySQL) を許可するセキュリティグループを設定します。
  • IAMロールの適切な権限: Lambda関数がVPC内でネットワークインターフェースを作成・管理するために必要な権限(ec2:CreateNetworkInterfaceec2:DescribeNetworkInterfacesec2:DeleteNetworkInterface)を、関数の実行ロールに付与します。
  • NAT GatewayまたはVPCエンドポイントの検討 (インターネットアクセスが必要な場合): もしVPCに関連付けられたLambda関数が、外部のAWSサービス(例: S3、DynamoDB)やインターネット上のAPIにアクセスする必要がある場合は、プライベートサブネットからインターネットに接続するためのNAT Gateway(パブリックサブネットに配置)またはVPCエンドポイントを設置する必要があります。VPCエンドポイントは、AWSサービスへのトラフィックをパブリックインターネットを経由させずにVPCネットワーク内で完結させるため、セキュリティをさらに強化できます。
  • ルーティングとネットワークACLの確認: 選択したサブネットのルートテーブルが、VPC内リソースやNAT Gateway/VPCエンドポイントへの適切なルーティングを持っていることを確認します。また、サブネットに適用されているネットワークACLが、Lambda関数に必要なトラフィックを許可していることを確認します。

修復方法

AWSコンソールでの修復手順

AWSコンソールを使用して、既存のLambda関数をVPCに関連付けます。

  1. Lambdaサービスへ移動: AWSコンソールにログインし、AWS Lambda サービスを開きます。
  2. 対象のLambda関数の選択: VPCに関連付けたいLambda関数を選択します。
  3. 設定タブへ移動: 関数の詳細ページで、「設定」タブをクリックします。
  4. VPCセクションの編集: 左側のナビゲーションペインで「VPC」を選択し、「編集」をクリックします。
  5. VPC、サブネット、セキュリティグループの設定:
    • VPC: ドロップダウンメニューから、関数を関連付けたいVPCを選択します。
    • サブネット: 関数をデプロイするVPC内のプライベートサブネットを1つ以上選択します。高可用性を考慮し、異なるアベイラビリティゾーンにある複数のサブネットを選択することを推奨します。
    • セキュリティグループ: Lambda関数に適用したいセキュリティグループを1つ以上選択します。これらのセキュリティグループは、関数がアクセスする必要のあるVPC内リソースへのアウトバウンドアクセスを許可するように設定されている必要があります。
  1. 保存: 設定を確認し、「保存」をクリックします。 Lambda関数がVPCに関連付けられるには数分かかる場合があります。この間、関数は一時的に利用できなくなることがあります。
  2. IAM実行ロールの確認: Lambda関数の実行ロールに、VPCにアクセスするための以下のIAMポリシーがアタッチされていることを確認してください。
    • AWSLambdaVPCAccessExecutionRole (AWS管理ポリシー) または同等のカスタムポリシー。
      • 必要な権限: ec2:CreateNetworkInterface, ec2:DescribeNetworkInterfaces, ec2:DeleteNetworkInterface

Terraformでの修復手順

TerraformでLambda関数をVPCに関連付けるには、aws_lambda_function リソースの vpc_config ブロックを設定します。

# (例) Lambda関数がアクセスするプライベートサブネット
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "private_subnet_a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "${aws_vpc.main.instance_tenancy}a" # 例えば ap-northeast-1a
  tags = {
    Name = "private-subnet-a"
  }
}

resource "aws_subnet" "private_subnet_b" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "${aws_vpc.main.instance_tenancy}b" # 例えば ap-northeast-1b
  tags = {
    Name = "private-subnet-b"
  }
}

# (例) Lambda関数がVPC内リソース (RDSなど) にアクセスするためのセキュリティグループ
resource "aws_security_group" "lambda_sg" {
  name        = "lambda-vpc-sg"
  description = "Security group for Lambda functions in VPC"
  vpc_id      = aws_vpc.main.id

  # 例: RDS (PostgreSQL) へのアウトバウンドアクセスを許可
  egress {
    from_port   = 5432 # RDS PostgreSQL ポート
    to_port     = 5432
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"] # VPC CIDR全体 (またはRDSサブネットのCIDR)
  }

  tags = {
    Name = "lambda-vpc-sg"
  }
}

# Lambda関数の実行ロールにVPCアクセス権限を付与
resource "aws_iam_role" "lambda_execution_role" {
  name = "lambda-vpc-execution-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Effect = "Allow",
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_basic_execution_policy" {
  role       = aws_iam_role.lambda_execution_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# VPCアクセスに必要な権限を付与
resource "aws_iam_role_policy_attachment" "lambda_vpc_access_policy" {
  role       = aws_iam_role.lambda_execution_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}

# Lambda関数の定義
resource "aws_lambda_function" "my_vpc_lambda" {
  function_name    = "my-vpc-enabled-lambda"
  handler          = "index.handler"
  runtime          = "nodejs18.x" # 使用するランタイムに合わせる
  role             = aws_iam_role.lambda_execution_role.arn
  timeout          = 30 # 秒
  memory_size      = 128 # MB

  filename         = "lambda_function_payload.zip" # コードを含むzipファイルのパス
  source_code_hash = filebase64sha256("lambda_function_payload.zip") # コードの変更を検知するためのハッシュ

  # Lambda関数をVPCに関連付ける設定
  vpc_config {
    subnet_ids         = [aws_subnet.private_subnet_a.id, aws_subnet.private_subnet_b.id]
    security_group_ids = [aws_security_group.lambda_sg.id]
  }

  tags = {
    Environment = "Production"
  }
}

# Lambdaコードのzipファイル (例: index.js を含む)
# プロジェクトのルートに lambda_function_payload.zip を配置
# index.js の例:
# exports.handler = async (event) => {
#   console.log('Event:', JSON.stringify(event, null, 2));
#   // ここにVPC内のリソースにアクセスするコードを記述
#   const response = {
#     statusCode: 200,
#     body: JSON.stringify('Hello from Lambda in VPC!'),
#   };
#   return response;
# };

上記のTerraformコードでは、aws_lambda_function リソースの vpc_config ブロックで Lambda関数をVPCに関連付けています。

  • subnet_ids: 関数をデプロイするプライベートサブネットのIDをリストで指定します。
  • security_group_ids: 関数に適用するセキュリティグループのIDをリストで指定します。
  • aws_iam_role_policy_attachment.lambda_vpc_access_policy: Lambda関数の実行ロールに、VPCアクセスに必要な AWSLambdaVPCAccessExecutionRole 管理ポリシーをアタッチしています。

lambda_function_payload.zip は、実際のLambda関数コードを含むzipファイルへのパスに置き換えてください。また、VPC、サブネット、セキュリティグループのCIDRブロックやIDも、ご自身の環境に合わせて適切に設定してください。

最後に

この記事では、Lambda関数をVPCに関連付けることの重要性について解説しました。Lambda関数をVPC内に配置することで、プライベートリソースへのセキュアなアクセスを可能にし、ネットワークセキュリティ制御を強化し、コンプライアンス要件を満たすことができます。

貴社のLambda関数は、VPCに適切に関連付けられていますか?もしVPC内リソースへのアクセスが必要な場合や、より厳格なネットワーク分離が必要な場合は、ぜひこの機会に設定を確認・強化してみてください。

こちらの内容の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。運用が非常にラクに出来る製品になっていますのでぜひ興味がある方はお問い合わせお待ちしております。

最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る