セキュリティリスク回避のためのRDS(MySQL)へのIAM認証導入 Spring Boot編

はじめに

最近、RDSのIAM認証を実装しています。

MySQLは以下のものを。

miyohide.hatenablog.com

PostgreSQLは以下のものを。

miyohide.hatenablog.com

上記ではコマンドライン上での操作を実装してみたのですが、アプリケーションとして実装する方法を検証してみます。

前提事項

以下の環境で実装しました。

実装

実装として、MySQL Connector/J driverの代わりにAmazon Web Services JDBC Driver for MySQLを使います。

awslabs.github.io

具体的には、build.gradleに以下の2行を追加します。

runtimeOnly 'software.aws.rds:aws-mysql-jdbc:1.1.14'
runtimeOnly 'software.amazon.awssdk:rds:2.25.50'

既存のruntimeOnly 'com.mysql:mysql-connector-j'はコメント化しました。

その後、application.propertiesに対して接続先の設定をします。JDBC Driverを変えたので、spring.datasource.driver-class-nameの修正が必要です。具体的には以下のような記述になりました。

spring.datasource.driver-class-name=software.aws.rds.jdbc.mysql.Driver
spring.datasource.url=jdbc:mysql://エンドポイント:3306/データベース名
spring.datasource.username=IAMユーザー名
spring.datasource.hikari.data-source-properties.useAwsIam=true

IAMユーザー認証を有効にするために、spring.datasource.hikari.data-source-properties.useAwsIamtrueに設定する必要があります。これは、コネクションプールライブラリとしてデフォルトであるHikari CPを使っている例です。他のコネクションプールライブラリとして別のものを使っている場合は別の設定となると思います。

以上の設定をすることで、Spring Bootで作ったアプリケーションにてAmazon RDS for MySQLに対してIAM認証を実装することができました。

考察

IAM認証用に使用するライブラリやapplication.propertiesに対して変更が発生することがわかりました。ローカル上でテストする際にはpropertiesファイルを切り替えるなど何らかの対応が必要かなと思います。Spring BootのProfileの機能を使う必要があるかと思います。

docs.spring.io

また、IAMユーザーに対してアクセスするデータベースに対して必要な権限がないと接続に失敗します。地味にはまりポイントなので、まずはコマンドラインで動作確認をすると良いかなと思います。

今回はMySQLで実装しましたが、PostgreSQLでの実装も考えています。同様のJDBC DriverがAWSよりリリースされているのでこれを試すのかなと考えています。

aws.amazon.com

LayerにGemを格納してAWS Lambda関数を成功させる方法(3)Ruby 3.3版

はじめに

先日、Rubyで実装したAWS Lambda関数にてpg gemを使う方法を記しました。

miyohide.hatenablog.com

上記の記事では、Ruby 3.2のバージョンで実装したのですが、この記事を記したのちにRuby 3.3版がリリースされました。

aws.amazon.com

Ruby 3.3版においてはベースとなるOSもAmazon Linux 2023に変わっているので、上記記事で記したDockerfileなどの記述も変更が必要になると考えられます。ここでは、Ruby 3.3版でpg gemを使う方法を記します。

Layerを作るためのコンテナイメージを作る

Layerを作るためのコンテナイメージを作ります。以下のようなDockerfileを用意します。

FROM public.ecr.aws/sam/build-ruby3.3:latest-x86_64

WORKDIR /var/task

RUN dnf install -y postgresql15 libpq-devel
RUN gem update bundler
CMD ["bash", "make_layer.sh"]

Base ImageをRuby 3.3のものにし、インストールするPostgreSQL関連ライブラリを若干変えています。

pg gemを使うために必要なライブラリ等をコピーするshell scriptであるmake_layer.shを以下の中身で用意します。

bundle config --local silence_root_warning true
bundle config set clean true
bundle config set path '/var/task'

bundle install

mkdir -p /var/task/lib

cp -a /usr/lib64/libpq.so.5.15 /var/task/lib/libpq.so.5
cp -a /usr/lib64/liblber-2.4.so.2.11.5 /var/task/lib/liblber-2.4.so.2
cp -a /usr/lib64/libldap_r-2.4.so.2.11.5 /var/task/lib/libldap_r-2.4.so.2
cp -a /usr/lib64/libsasl2.so.3.0.0 /var/task/lib/libsasl2.so.3
cp -a /usr/lib64/libssl3.so /var/task/lib/libssl3.so
cp -a /usr/lib64/libsmime3.so /var/task/lib/libsmime3.so
cp -a /usr/lib64/libnss3.so /var/task/lib/libnss3.so
cp -a /usr/lib64/libnssutil3.so /var/task/lib/libnssutil3.so

cd /var/task
zip -r layer.zip .

Ruby 3.2版と比べて幾つかのライブラリのバージョンが微妙に変わっています。

あとは、docker build -t ruby-lambda-layer .を実行してコンテナイメージを作成し、docker run --rm -it -v $PWD:/var/task ruby-lambda-layerを実行するとzipファイルが作成されます。

環境変数の設定

Lambdaの環境変数としてGEM_HOME.opt/ruby/3.3.0を設定しておきます。Ruby 3.2版で必要だったLD_PRELOADは必要ありませんでした。

実行

単純にrequire 'pg'としただけの以下のプログラムで動作確認をします。

無事、正常終了しました。

考察

先日の記事にも記した通り、動作にはLambdaの動作環境に強く依存したライブラリのコピーが必要となり、動作環境の変更が発生すると微妙なバージョン番号が変わっているのは地味に大変です。

RubyにおいてはLambdaで動くコンテナイメージを作っておいた方が考えることが少なくて良いと思われます。

コンテナイメージのつくり方は以下を参照すると良いかなと思います。

miyohide.hatenablog.com

セキュリティリスク回避のためのRDS(PostgreSQL)へのIAM認証導入

はじめに

先日、RDS(MySQL)へのIAM認証を導入したことをブログに書きました。

miyohide.hatenablog.com

今回は、RDS(PostgreSQL)へのIAM認証を導入してみます。

また、この検証をするためにCDKも実装してみたのでそれも紹介します。

公式ドキュメントの確認

公式ドキュメントは以下で公開されているのでそれをもとに作業を進めます。

docs.aws.amazon.com

CDKでの実装

IAMポリシー作成

まずはIAMポリシーを作成します。公式ドキュメントは以下のもの。

docs.aws.amazon.com

CDKでは以下のように実装しました。

    // IAMロールを作成
    const role = new Role(this, 'Role', {
      roleName: 'EC2RoleForRDSIAMAuth',
      assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
    });

    // RDSへのIAM認証に必要なポリシーをアタッチ
    role.addToPolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: ["rds-db:connect"],
      resources: ["*"]
    }));

対象のresources['*']としていますが、ここは改善したいところ。

EC2作成

RDS(PostgreSQL)に接続するEC2を作成します。クライアントとしてpostgresql15をインストールする処理をユーザーデータに指定しておき、先ほど作成したIAMポリシーも指定しておきます。

    const userData = UserData.forLinux({
      shebang: '#!/bin/bash',
    });
    userData.addCommands(
      'dnf install -y mariadb105',
      'dnf install -y postgresql15'
    );

    // EC2インスタンスを作成する
    const ec2Instance = new Instance(this, 'EC2Instance', {
      vpc: vpc,
      vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
      securityGroup: ec2Sg,
      instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MICRO),
      machineImage: new AmazonLinuxImage({
        generation: AmazonLinuxGeneration.AMAZON_LINUX_2023,
      }),
      userData: userData,
      role: role,
    });

RDS(PostgreSQL)の操作

作成しているRDS(PostgreSQL)にIAM認証を導入します。まずはIAM認証を有効化することを忘れずに。

docs.aws.amazon.com

また、データベースアカウントを作成します。公式ドキュメントは以下。

docs.aws.amazon.com

特段難しいことはなく、CREATE USER ユーザー名;を実行後、GRANT rds_iam TO ユーザー名;でユーザーにrds_iamロールを付与すればOKです。これだけだとちょっと不便なのでALTER ROLE ユーザー名 CREATEDB;を実行してデータベースを作れるようにしておくと良いかなと思います。\duを実行してロールを確認しておくと良いかなと思います。

データベース名=> \du
                                                                                                                 List of roles
    Role name    |                         Attributes                         |                                                                            Memb
er of                                                                            
-----------------+------------------------------------------------------------+--------------------------------------------------------------------------------
---------------------------------------------------------------------------------
 ユーザー名        | Create DB                                                  | {rds_iam}
 postgres        | Create role, Create DB                                    +| {rds_superuser}
                 | Password valid until infinity                              | 
 rds_ad          | Cannot login                                               | {}
 rds_iam         | Cannot login                                               | {}
 rds_password    | Cannot login                                               | {}
 rds_replication | Cannot login                                               | {}
 rds_superuser   | Cannot login                                               | {pg_read_all_data,pg_write_all_data,pg_monitor,pg_signal_backend,pg_checkpoint,
pg_use_reserved_connections,pg_create_subscription,rds_replication,rds_password}
 rdsadmin        | Superuser, Create role, Create DB, Replication, Bypass RLS+| {}
                 | Password valid until infinity                              | 
 rdsrepladmin    | No inheritance, Cannot login, Replication                  | {}
 rdstopmgr       |                                                            | {pg_monitor,pg_checkpoint}

データベース名=>

以上で設定は終了です。

IAM認証で接続する

環境変数に接続先とアクセストークンを格納しておきます。

$ export RDSHOST="RDSのエンドポイント"
$ export PGPASSWORD="$(aws rds generate-db-auth-token --hostname $RDSHOST --port 5432 --region リージョン --username ユーザー名)"

以下のコマンドで接続します。

$ psql "host=$RDSHOST port=5432 dbname=データベース名 user=ユーザー名 password=$PGPASSWORD"
psql (15.6, server 16.1)
WARNING: psql major version 15, server major version 16.
         Some psql features might not work.
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

データベース名=>

無事接続できました。

考察

今回はPostgreSQLに対してIAM認証を導入してみました。細かいコマンドは違いますが、ほぼほぼMySQLと同じ感じです。

今回は手作業を解消するためにCDKを導入しました。先日手作業で実施したところがミスなく環境を構築することができました。CDKの実装にはそこそこ時間がかかりますが、Web上でいろんな画面を行ったり来たりするよりも格段に楽なので、CDKでの実装しておくと良いかなと考えています。

今後は、実際のアプリでIAM認証を導入していくことをやっていきます。

Amazon Linux 2023でのプライベートサブネットへのパッケージインストール方法

はじめに

プライベートサブネットに配置しているEC2に対してインストールしたいアプリやセキュリティパッチなどを当てたい場合、一般的にはインターネットに接続する必要があります。

ただ、Amazon Linuxにおいてはインターネットにアクセスせずに更新したりパッケージのインストールができたりします。詳細は以下を参照してください。

repost.aws

以下では、Amazon Linux 2023においてプライベートサブネットにおいたインスタンスにパッケージのインストールをすることをCDKで実装してみます。

VPC作成

VPCを作成します。natGatewaysを0にしておきます。

    const vpc = new Vpc(this, 'VPC', {
      cidr: '10.0.0.0/16',
      vpcName: 'MyRDSTestVPC',
      natGateways: 0,
      maxAzs: 2,
      subnetConfiguration: [
        {
          // EC2用のprivate subnet
          cidrMask: 24,
          name: 'forEC2',
          subnetType: SubnetType.PRIVATE_WITH_EGRESS
        }
      ]
    });

S3へのアクセスパスを設定

GatewayタイプのVPCエンドポイントを追加します。

    // GatewayタイプのVPCエンドポイントを作成する
    vpc.addGatewayEndpoint('s3Endpoint', {
      service: GatewayVpcEndpointAwsService.S3,
      subnets: [{ subnetType: SubnetType.PRIVATE_WITH_EGRESS }]
    });

準備は以上で完了です。

EC2作成時にパッケージのインストールを実行する

EC2作成時に適当にパッケージをインストールする処理を書いてみます。詳細は以下のドキュメントにあるユーザーデータを使用します。

docs.aws.amazon.com

CDKでの実装は以下の通り。今回はMariaDBPostgreSQLをインストールしてみます。

    const userData = UserData.forLinux({
      shebang: '#!/bin/bash',
    });
    userData.addCommands(
      'dnf install -y mariadb105',
      'dnf install -y postgresql15'
    );

dnfコマンドに-yオプションをつけておく必要があります。これをつけていない場合はインストールがされません。

EC2インスタンスの作成

EC2インスタンスを作成します。上記で作成したuserDatauserDataプロパティの値として指定しておきます。

    // EC2インスタンスを作成する
    const ec2Instance = new Instance(this, 'EC2Instance', {
      vpc: vpc,
      vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
      securityGroup: ec2Sg,
      instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.MICRO),
      machineImage: new AmazonLinuxImage({
        generation: AmazonLinuxGeneration.AMAZON_LINUX_2023,
      }),
      userData: userData,
    });

以上を実行することでEC2インスタンスMariaDBPostgreSQLがインストールされています。

確認

ユーザーデータの実行結果は、/var/log/cloud-init-output.logに出力されます。確認してみると、無事dnfコマンドが実行されていることが確認できます。

コマンドも無事インストールされていることが確認できました。

考察

プライベートサブネットに配置したAmazon Linux 2023に対してインターネットに接続しないままパッケージのインストールすることを実装しました。NatGatewayは地味にお金がかかるサービスなので、必要なければ作らない方が良いかなと考えています。

パッケージのインストールのために今回はユーザーデータを使用しました。ユーザーデータ内の処理が失敗してもCDK自身の処理は正常終了するので、何らかの形で確認する必要はあります。

セキュリティリスク回避のためのRDS(MySQL)へのIAM認証導入

はじめに

Amazon RDSではユーザー名とパスワード以外にIAM認証という機能があります。RDSなどの接続用パスワードをソースコードに書き込むのはよくあるセキュリティリスクの一つではあるんですが、環境変数やParameter Store、Secrets Managerに格納するのはそれはそれで面倒臭いです。そこで、IAM認証という機能を使ってパスワードを使わないようにします。

今回は、MySQLでEC2からCLI接続するときの認証をIAM認証を使ったケースを試してみます。

準備

EC2からMySQLに接続するためのCLIをインストールします。以下を参照しますが、MariaDBクライアントを利用します。

docs.aws.amazon.com

今後の作業用のために環境変数としてDB_ENDPOINTIAM_USERNAMEを指定します。

export DB_ENDPOINT=RDSのエンドポイント
export IAM_USERNAME=IAM認証するユーザ名

手順

具体的な手順は以下のre:Postにて公開されていますので、この手順に従って作業を進めます。

repost.aws

IAM認証の有効化

まず、RDSのインスタンス作成時や変更でIAM認証を有効化しておきます。

IAM認証を有効化してない場合、以下のSQL文の実行時にエラーメッセージERROR 1524 (HY000): Plugin 'AWSAuthenticationPlugin' is not loadedが出力されます。

CREATE USER IAM認証するユーザ名 IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS';

SSLを使用して接続するように設定します。

ALTER USER IAM認証するユーザ名 REQUIRE SSL;

IAMポリシーの作成

接続の許可を設定するためにIAMポリシーを設定します。詳細は以下を確認します。

docs.aws.amazon.com

作成したIAMポリシーをもとにIAMロールを作成し、EC2にアタッチすると準備はOKです。

証明書を取得する

接続時に指定する証明書をダウンロードします。上記のre:Postの記事にある証明書を使って接続しようとするとself signed certificate in certificate chainというエラーメッセージがでて接続できませんでした。そのため、以下のリンク先からリンクが貼られている証明書から使用しているAWSリージョンにあった証明書をダウンロードします。

docs.aws.amazon.com

今回の場合、東京リージョンで実施したので、wget https://truststore.pki.rds.amazonaws.com/ap-northeast-1/ap-northeast-1-bundle.pemを実行しました。

認証トークンを生成する

AWS CLIを使って認証トークンを取得して環境変数TOKENに保存します。以下のコマンドを実行します。

TOKEN=`aws rds generate-db-auth-token --hostname $DB_ENDPOINT --port 3306 --username $IAM_USERNAME`

接続する

以下のコマンドを実行します。

mariadb -h $DB_ENDPOINT  -u $IAM_USERNAME --ssl-ca=ap-northeast-1-bundle.pem --password=$TOKEN

無事、接続できました。

[ec2-user@ip-xxx-xxx-xxx-xxx ~]$ mariadb -h $DB_ENDPOINT  -u $IAM_USERNAME --ssl-ca=ap-northeast-1-bundle.pem --password=$TOKEN
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 39
Server version: 8.0.35 Source distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> 

考察

初期セットアップは思いのほか大変ですが、パスワードを取り回さなくてもよいのは非常に楽です。

今回はMySQLのクライアントで試してみましたが、元々はプログラム内でRDSのパスワードを保持したくないという思いがあります。これを実装してみたいです。

また、PostgreSQLもIAM認証に対応しているのでPostgreSQL版も試してみます。

無償版LocalStackでのRuby SDKを用いたS3操作検証方法

はじめに

AWS上でのシステムを構築する際に簡単な挙動をローカル環境で確認したいという要望はちょくちょく聞きます。そのときによく使われるのがLocalStackです。

www.localstack.cloud

有償版・無償版それぞれあるのですが、無償版でも多くの機能が使えます。詳細は以下を参照。

docs.localstack.cloud

今回はこのLocalStackの無償版を使ってRuby SDKでS3の操作を試してみます。

前提

以下のバージョンで試しました。

  • LocalStack 3.3.0
  • Ruby 3.3.0
  • AWS SDK S3 gem 1.146.1

LocalStackの準備

LocalStackのセットアップはDockerを使うと簡単です。以下のようなcompose.ymlを作成しました。

version: "3.8"

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
    image: localstack/localstack:3.3.0
    ports:
      - "127.0.0.1:4566:4566"
    environment:
      - DEBUG=${DEBUG:-0}
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
  app:
    build: .
    tty: true
    volumes:
      - .:/myapp/

アプリの準備

アプリ検証用としてDockerfileも用意しておきます。

FROM ruby:3.3.0-slim

WORKDIR /myapp

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock

RUN bundle install

COPY lib /myapp/lib

gemの管理用としてGemfileを以下の内容で作成します。

source 'https://rubygems.org'

gem 'aws-sdk-s3', '~> 1.146.1'

Gemfile.lockは空ファイルとして作成します。

アプリはlib/app.rbとして準備しておきます。今回はこんな感じで。

require 'aws-sdk-s3'

region = "us-east-1"
bucket_name = "bucket01"
key_name = "key01"

Aws.config.update(
  endpoint: "http://localstack:4566",
  access_key_id: "test",
  secret_access_key: "test",
  region: region,
  force_path_style: true
)

client = Aws::S3::Client.new

# バケットを作成
client.create_bucket(bucket: bucket_name)
p client.list_buckets

# オブジェクトを作成
client.put_object(bucket: bucket_name, key: key_name, body: "Hello World")

# オブジェクトを読み込み
p client.get_object(bucket: bucket_name, key: key_name).body.read

# オブジェクト情報一覧
p client.list_objects_v2(bucket: bucket_name)

実行

準備が整えば、docker compose upを実行するとアプリが起動します。起動しているコンテナのうち、appコンテナに接続してruby lib/app.rbを実行するとAWS SDK S3 gemを利用したS3の動作が確認できます。

## ↓ client.list_bucketsの実行結果
#<struct Aws::S3::Types::ListBucketsOutput buckets=[#<struct Aws::S3::Types::Bucket name="bucket01", creation_date=2024-04-07 04:47:48 UTC>], owner=#<struct Aws::S3::Types::Owner display_name="webfile", id="xxxxx">>
## ↓ client.get_objectの実行結果
"Hello World"
## ↓ client.list_objects_v2の実行結果
#<struct Aws::S3::Types::ListObjectsV2Output is_truncated=false, contents=[#<struct Aws::S3::Types::Object key="key01", last_modified=2024-04-07 05:18:59 UTC, etag="\"yyyyy\"", checksum_algorithm=[], size=11, storage_class="STANDARD", owner=nil, restore_status=nil>], name="bucket01", prefix="", delimiter=nil, max_keys=1000, common_prefixes=[], encoding_type=nil, key_count=1, continuation_token=nil, next_continuation_token=nil, start_after=nil, request_charged=nil>

考察・注意点

LocalStackはあくまで他ベンダーによるエミュレータですので、挙動がAWSのものと一致しているとは限らないのは注意が必要です。

また、開発用としてAws.config.updateendpointaccess_key_idなどは個別に設定する必要があり、環境変数化などのなんらかの対処は必要になるかなと思います。

最後に、Aws.config.updateで指定しているforce_path_style: trueについてです。S3はPath styleとVirtual hosted styleの2種類のアドレスモデルがあり、Path styleは廃止予定となっています。

aws.amazon.com

force_path_style: trueの記述は、Path styleでアクセスすることを指定するものです。この記述がない場合、以下のエラーメッセージが出力されます。

/usr/local/lib/ruby/3.3.0/net/http.rb:1603:in `initialize': Failed to open TCP connection to bucket01.localstack:4566 (getaddrinfo: Name or service not known) (Seahorse::Client::NetworkingError)

なんらかの回避方法を探しているのですが、この記事を書いているときには見つけることができませんでした。

AWS SAMを使ったAWS Lambda関数のVPC設定

はじめに

ちょっと前までAWS Lambda上で動くアプリをJavaで実装することをやっていました。

このアプリの特性として、S3やRDSと接続することになるのですが、これらの環境をマネジメントコンソールで操作するのは面倒くさいので、AWS SAMを使って実装してみることにします。

VPCの設定

マネジメントコンソール上の設定で面倒なのは、VPCの設定です。AWS SAMのドキュメントを見るとプロパティの最後にVpcConfigがあるのでこれを設定することになります。

docs.aws.amazon.com

ここでは、必要なVPCAWS SAMのテンプレートファイル(template.yml)にて作成するようにします。

  vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: AWS-Flayway-Lambda-VPC
  private1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref vpc
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: AWS-Flayway-Lambda-private1
  private2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId: !Ref vpc
      AvailabilityZone: !Select [1, !GetAZs '']
      Tags:
        - Key: Name
          Value: AWS-Flayway-Lambda-private2
  # route table
  routeTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref vpc
      Tags:
        - Key: Name
          Value: AWS-Flayway-Lambda-route-table
  # route tableのアサイン
  subnet1Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref private1
      RouteTableId: !Ref routeTable
  subnet2Association:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref private2
      RouteTableId: !Ref routeTable
  # VPC LambdaからS3にアクセスするためのVPCエンドポイント
  vpcEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcId: !Ref vpc
      VpcEndpointType: Gateway
      ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
      RouteTableIds:
        - !Ref routeTable
  # Lambdaが使用するセキュリティグループ
  securityGroupForLambda:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: security-group-for-lambda
      GroupDescription: For Lambda
      VpcId: !Ref vpc
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          IpProtocol: -1

ここで指定したサブネットやセキュリティグループを使ってVpcConfigを以下のように指定します。

  function:
    Type: AWS::Serverless::Function
    # 関数の定義
    Properties:
    # 中略
      VpcConfig:
        SecurityGroupIds:
          - !GetAtt securityGroupForLambda.GroupId
        SubnetIds:
          - !Ref private1
          - !Ref private2

マネジメントコンソールを使った設定で、毎回つまづくのがAWSLambdaVPCAccessExecutionRoleの不足によりVPC設定ができないということです。AWS SAMのテンプレートのPoliciesを使って必要なロールを設定しておきます。

  function:
    Type: AWS::Serverless::Function
    # 関数の定義
    Properties:
      # 省略
      Policies:
        - AWSLambdaBasicExecutionRole
        - AWSLambda_ReadOnlyAccess
        - AWSXrayWriteOnlyAccess
        - AWSLambdaVPCAccessExecutionRole
        - AmazonS3ReadOnlyAccess
     # 以下略

以上の設定からスムーズにLambda関数のVPC設定を行うことができ、またS3との接続も容易にできるようになりました。

考察

今回はAWS SAMを使ったAWS Lambda関数のVPC設定を実装しました。大量にあるプロパティの中からVPC設定の部分をマニュアルから見つけるのは難しいのですが、最近(2023年11月に)入った以下の更新で、AWS SAMの設定をSAMテンプレートファイルとしてダウンロードできるようになりました。

aws.amazon.com

今回はこの機能を使って、まずはマネジメントコンソールで設定した後、VpcConfigの設定を実装してみました。このような機能を使うといわゆるIaCも簡単に実装できるのかなと考えています。