phpmyadmin-with-basic-auth を AWS Lambda Web Adapter で動作させるための構成です。
PMA_SSL 環境変数に 1 を設定することで SSL/TLS 設定を有効にしてサーバに接続するようになります。
PlanetScale などの DBaaS に接続する際に設定します。
コンテナは 8080 ポートでリッスンします (Lambda Web Adapter のデフォルトポート)。
CloudFront (CDN) → Lambda Function URL → Lambda (phpMyAdmin + Lambda Web Adapter)
- Lambda Web Adapter: Lambda 呼び出しを HTTP リクエストに変換してコンテナ内の Apache へ転送します。
- CloudFront: CDN として Lambda Function URL の前段に配置します。
-
VPCアクセス(Lambda VPC 配置時)
subnet_idsとsecurity_group_idsを指定することで Lambda を VPC 内に配置し、プライベートサブネットの RDS / Aurora に接続できます。- Lambda を VPC 内に配置すると、外部インターネットへのアウトバウンド通信がデフォルトでブロックされます。外部の DBaaS(PlanetScale など)に接続する場合は、VPC に NAT Gateway を設定する必要があります。詳細は AWS ドキュメント - Lambda による VPC ネットワーキング を参照してください。
- VPC 内の RDS/Aurora にのみ接続する場合は NAT Gateway 不要です。
-
Lambda 読み取り専用ファイルシステム
Lambda のコンテナ実行環境ではルートファイルシステムが読み取り専用にマウントされます。実行時に書き込めるのは
/tmpのみです。この制約への対処として、以下の設定が Dockerfile に組み込まれています:対象 デフォルトパス(変更前) Lambda 向け変更後 Apache PID ファイル /var/run/apache2/apache2.pid/tmp/apache2.pidApache ロック/Mutex ディレクトリ /var/lock/apache2/tmp/apache2-locksPHP セッション保存先 /var/lib/php/sessions//tmpphpMyAdmin TempDir /var/www/html/tmp//tmp/phpmyadminconfig.secret.inc.phpエントリポイントが実行時に生成 Dockerfile でビルド時に生成済み コールドスタートごとに
/tmpは空の状態でマウントされるため、必要なサブディレクトリ(/tmp/apache2-locks、/tmp/phpmyadmin)はエントリポイント内でmkdir -pにより作成しています。また、上流イメージのエントリポイントには起動時に
chgrp www-data /etc/phpmyadmin/config.secret.inc.phpを実行するコードが含まれており、読み取り専用ファイルシステム上ではEPERMで失敗します。このリポジトリではカスタムエントリポイント(docker-entrypoint.sh)でその行を除去し、config.secret.inc.phpをビルド時に生成することで問題を回避しています。 -
Apache ユーザー名解決(AH00543)
Lambda の一部実行環境では
getpwnam("www-data")が NULL を返し、Apache が以下のエラーで起動に失敗します:AH00543: httpd: bad user name www-dataこれを回避するため、Dockerfile では Apache ワーカープロセスをユーザー名ではなく数値 UID/GID(
#33)で実行しています:ENV APACHE_RUN_USER=#33 \ APACHE_RUN_GROUP=#33 -
イメージアーキテクチャ(amd64 / arm64)
phpmyadmin/phpmyadminの公式イメージはlinux/amd64のみ提供されており、linux/arm64向けに直接ビルドしようとすると QEMU が ELF バイナリを実行できず失敗します。このリポジトリのビルドワークフローでは、アーキテクチャごとに異なるベースイメージを使うマトリクスビルドで両プラットフォームに対応しています:
プラットフォーム ベースイメージ linux/amd64phpmyadmin/phpmyadmin:5.2.3linux/arm64arm64v8/phpmyadmin:5.2.3各アーキテクチャ向けのイメージはタグサフィックス(
-amd64/-arm64)付きで ECR Private と GHCR に Push されます。 -
Docker イメージビルド時の OCI メディアタイプ問題
docker/build-push-actionv4 以降はデフォルトで Provenance Attestation(provenance: true)を有効にしており、OCI イメージインデックス形式のマニフェストが生成されます。Lambda はこの形式をサポートしないため、ビルドワークフローではprovenance: falseを明示的に指定しています:- uses: docker/build-push-action@... with: provenance: false
-
Lambda のタイムアウト・メモリ設定
phpMyAdmin はコールドスタート時の初期化に時間がかかる場合があります。
example-terraformではデフォルト値として以下を設定しています:設定 値 タイムアウト 30 秒 メモリサイズ 512 MB 利用状況に応じて調整してください。
-
Lambda の制約による潜在的な課題
Lambda は本質的に短時間・軽量な処理向けに設計されており、大容量・長時間処理には適していません。phpMyAdmin を Lambda で利用する際は以下の制約に注意してください:
制約 内容 タイムアウト Lambda の最大実行時間は 15 分。大きなテーブルのエクスポート・インポートなど時間のかかる操作はタイムアウトで中断される可能性があります。 レスポンスペイロードサイズ Lambda Function URL のレスポンスは最大 6 MB(バッファリングモード)。大量データの SELECT 結果など、レスポンスが 6 MB を超える操作は失敗します。 レスポンスストリーミングと VPC の非互換 レスポンスストリーミング( RESPONSE_STREAM)モードでは 6 MB 制限を緩和できますが、VPC 内配置の Lambda ではストリーミングは利用できません。VPC 配置(RDS/Aurora 接続)と大容量レスポンスの両立は現状困難です。
Lambda Function URL はパブリックに公開されています。直接アクセスを防ぐために、CloudFront とLambda の両側で多層防御を実装しています。
CloudFront → (X-Origin-Verify: <secret>) → Lambda Function URL → PHP (auto_prepend_file) → Apache → phpMyAdmin
↑
ヘッダが一致しない場合 403 を返す
- CloudFront がオリジンへリクエストを転送する際に
X-Origin-Verifyヘッダにシークレット値を付加します(cloudfront_origin_secretvariable)。 - PHP の
auto_prepend_file(cookie-session-handler.php)がCLOUDFRONT_ORIGIN_SECRET環境変数と照合し、一致しない場合は HTTP 403 を返します。 - CloudFront は
X-Origin-Verifyヘッダをビューアへは転送しないため、シークレット値が外部に漏れません。
CloudFront OAC は SigV4 署名を付与しますが、POST ボディのハッシュを計算しません(unsigned-payload 扱い)。
一方、Lambda Function URL は受信したボディのハッシュを自ら計算して SigV4 署名を検証するため、ボディハッシュが存在しない(unsigned-payload)署名を拒否し、POST リクエストが 403 エラーになります。
ボディハッシュを正しく含めた署名を生成するには Lambda@Edge など別途コンピューティングリソースが必要です。このテンプレートではそのコストを避けるためカスタムヘッダ方式を採用しています。
| リクエスト | OAC(AWS_IAM) | カスタムヘッダ(NONE) |
|---|---|---|
| GET | ✅ 成功(ボディなし) | ✅ 成功 |
| POST | ❌ ボディハッシュ未計算(unsigned-payload)で署名検証失敗 | ✅ 成功 |
参考: Restricting access to a Lambda function URL - Amazon CloudFront
このテンプレートでは Lambda 側に基本認証機能を実装していません。理由は以下の通りです:
- Lambda Function URL のヘッダ変換: Lambda Function URL 経由で返される
WWW-Authenticateヘッダがx-amzn-Remapped-www-authenticateに変換され、ブラウザの Basic 認証ダイアログが表示されないためです。 - Lambda 側での認証は無効: Apache で Basic 認証を設定しても、ブラウザが認証を要求できず、ユーザーは認証なしで (401 エラーで) ブロックされるだけになります。
基本認証やアクセス制限が必要な場合は、以下の代替手段を推奨します:
-
CloudFront Functions(推奨)
- CloudFront フロントで Basic 認証を実装
WWW-Authenticateヘッダをクライアントに返すことで、ブラウザのダイアログが正常に動作- サーバレス、低レイテンシ
-
AWS WAF
- CloudFront に紐付けて IP ホワイトリストや地域制限、レート制限などを適用
-
Amazon Cognito
- OAuth 2.0 / OpenID Connect による認証
- CloudFront + Lambda@Edge や ALB と組み合わせて実装
Lambda はステートレスな実行環境であるため、通常の PHP ファイルセッションは以下の問題を抱えています。
| 問題 | 説明 |
|---|---|
| コールドスタート | 新しいコンテナインスタンスが起動するたびに /tmp のセッションファイルが失われる |
| 複数インスタンス | 同時リクエストが別インスタンスで処理されると、互いのセッションを参照できない |
この構成では Cookie ベースのセッションハンドラー (src/cookie-session-handler.php) を実装しており、これらの問題を解決します。
セッションデータをサーバ側ではなくブラウザのクッキーに保存します。
ログイン → session_write → AES-256-CBC 暗号化 + HMAC-SHA256 署名 → Cookie に保存
次リクエスト → Cookie 取得 → HMAC 検証 → 復号 → session_read → phpMyAdmin に渡す
| 項目 | 仕様 |
|---|---|
| 暗号化 | AES-256-CBC |
| 鍵導出 | SHA-256(PMA_BLOWFISH_SECRET) → 32 バイト固定長 |
| 整合性 | HMAC-SHA256(Encrypt-then-MAC) |
| Cookie 属性 | HttpOnly, SameSite=Strict, HTTPS 時 Secure |
| HTTPS 判定 | $_SERVER['HTTPS'] および X-Forwarded-Proto: https(CloudFront)に対応 |
| クッキー名 | PMA_SESS |
注意: セッションデータはクッキーに保存されるため、セッションサイズが 4 KB 以内に収まる必要があります。phpMyAdmin の通常利用では問題ありません。
| 変数 | 必須 | 説明 |
|---|---|---|
PMA_BLOWFISH_SECRET |
○ | Cookie セッション暗号化キー。32 文字以上のランダム文字列を推奨 |
CLOUDFRONT_ORIGIN_SECRET |
△ | CloudFront カスタムオリジンヘッダと一致するシークレット。Lambda 環境では必須。未設定時はヘッダ検証をスキップ(ローカル開発向け) |
SESSION_SECRET |
× | PMA_BLOWFISH_SECRET のフォールバック。両方とも未設定の場合はファイルセッション(/tmp)にフォールバック |
PMA_SESS_DEBUG |
× | 1 に設定するとセッションハンドラーのデバッグログを error_log に出力します(トラブルシュート用) |
PMA_BLOWFISH_SECRET が未設定の場合、Cookie セッションハンドラーは登録されず、/tmp へのファイルセッションにフォールバックします(インスタンスローカルなステートフルセッション)。
docker-compose(ローカル開発)
services:
phpmyadmin:
build: .
environment:
PMA_ARBITRARY: 1
PMA_BLOWFISH_SECRET: "your-random-32-char-secret-here!!"
ports:
- "8080:8080"Terraform(Lambda 環境変数)
resource "aws_lambda_function" "phpmyadmin" {
# ...
environment {
variables = {
PMA_ARBITRARY = "1"
PMA_BLOWFISH_SECRET = var.pma_blowfish_secret # AWS Secrets Manager などから取得
CLOUDFRONT_ORIGIN_SECRET = var.cloudfront_origin_secret # AWS Secrets Manager などから取得
}
}
}PMA_BLOWFISH_SECRET および CLOUDFRONT_ORIGIN_SECRET は AWS Secrets Manager または SSM Parameter Store(SecureString)で管理し、Terraform var や Lambda の環境変数として注入することを推奨します。
src/- コンテナイメージのソースDockerfile- Lambda Web Adapter を組み込んだ phpMyAdmin イメージconfig.user.inc.php- phpMyAdmin SSL設定 & TempDir 設定cookie-session-handler.php- Cookie ベースのセッションハンドラー & オリジン検証docker-entrypoint.sh- カスタムエントリポイント(Lambda の読み取り専用ファイルシステム対応)docker-compose.yml- ローカル開発用 docker-compose
.github/workflows/docker-publish.yml- ECR Private / GHCR へのイメージ Push ワークフロー.github/dependabot.yml- 依存関係の自動更新設定ecr-terraform/- ECR Private リポジトリと GitHub Actions OIDC ロールの Terraformexample-terraform/- Lambda + CloudFront デプロイ例の Terraform
cd ecr-terraform
terraform init
terraform apply -var="region=ap-northeast-1"github_actions_role_arn と repository_uri の出力値を控えておきます。
| Variable | 説明 |
|---|---|
AWS_ROLE_ARN |
ecr-terraform で作成した GitHub Actions IAM ロール ARN |
AWS_REGION |
ECR Private を作成したリージョン (例: ap-northeast-1) |
ECR_REPOSITORY_URI |
ecr-terraform の repository_uri 出力値 |
main ブランチへの push または v*.*.* タグの作成で自動的にイメージが ECR Private と GHCR へ Push されます。
AWS Lambda は ECR Public のイメージと ECR pull through cache のイメージを直接実行できません。
public.ecr.aws/... や xxxxxxxxxxxx.dkr.ecr.<region>.amazonaws.com/ecr-public/... を image_uri に指定すると、以下のようなエラーになることがあります。
InvalidParameterValueException: The image manifest, config or layer media type for the source image is not supported.
そのため、このリポジトリでは最初から Lambda と 同じリージョンの private ECR に Push する構成にしています。ecr-terraform の region を Lambda のリージョンに合わせ、ワークフローでその repository_uri へ Push してください。
cd example-terraform
terraform init
terraform apply \
-var="region=ap-northeast-1" \
-var="image_uri=<account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/phpmyadmin-lambda-adapter:latest"pma_blowfish_secret と cloudfront_origin_secret を必ず設定してください。前者は Cookie セッションの AES-256 暗号化キー、後者は CloudFront ↔ Lambda 間のアクセス制御シークレットです。
terraform apply \
-var="image_uri=<account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/phpmyadmin-lambda-adapter:latest" \
-var='pma_blowfish_secret=<32文字以上のランダム文字列>' \
-var='cloudfront_origin_secret=<32文字以上の別のランダム文字列>'推奨: 両シークレットはバージョン管理から除外した
terraform.tfvarsまたは AWS Secrets Manager / SSM Parameter Store (SecureString) で管理してください。
# terraform.tfvars (.gitignore に追加してバージョン管理対象外にすること)
pma_blowfish_secret = "your-random-32-char-secret-here!!"
cloudfront_origin_secret = "another-random-32-char-secret!!!"src/docker-compose.yml: ローカル開発用 docker-compose の例example-terraform/: AWS Lambda + CloudFront にホスティングする Terraform コードの例