Amazon Elasticsearch を Lambda と IAM ロールで安全に制限する方法
はじめに
インターネット経由でアクセス可能な Amazon Elasticsearch ドメインへのアクセスを管理することは難しい場合があります。ドメインを VPC 内に配置すると、NAT Gateway や NAT Instance の追加コストが発生します。しかし、AWS Lambda 関数 をプロキシとして使用することで、コスト効率の良い安全なソリューションを実現できます。
この記事では、IAM ロールを設定した Lambda 関数からのみ Elasticsearch にアクセスを制限する方法を説明します。
前提条件
開始する前に、以下がインストールされていることを確認してください。
- AWS SAM CLI
- Python 3.x
ディレクトリ構成
以下は SAM アプリケーションの構造です。
/
|-- es-proxy-lambda/
| |-- __init__.py
| |-- lambda_function.py
| `-- requirements.txt
|-- samconfig.toml
`-- template.yaml
AWS SAM テンプレート
以下の AWS SAM テンプレート では、Elasticsearch ドメイン、Lambda 関数、および関連する IAM ロールなどのリソースを定義しています。
重要なポイント:
AccessPolicies
セクションは Elasticsearch ドメインへのアクセスを制限します。(8 行目から 16 行目)- Lambda 関数はドメインに対して特定のアクションを実行する権限を持ちます。(64 行目から 70 行目)
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Resources:
Elasticsearch:
Type: AWS::Elasticsearch::Domain
Properties:
AccessPolicies:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS:
- !GetAtt IamRole.Arn
Action: es:*
Resource: !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/es-for-lambda/*
DomainName: es-for-lambda
EBSOptions:
EBSEnabled: true
VolumeSize: 10
VolumeType: standard
ElasticsearchClusterConfig:
DedicatedMasterEnabled: false
InstanceCount: 1
InstanceType: t2.small.elasticsearch
ElasticsearchVersion: 7.4
Lambda:
Type: AWS::Serverless::Function
Properties:
CodeUri: es-proxy-lambda/
Environment:
Variables:
ES_DOMAIN: !GetAtt Elasticsearch.DomainEndpoint
FunctionName: es_proxy_lambda
Handler: lambda_function.lambda_handler
Role: !GetAtt IamRole.Arn
Runtime: python3.8
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub
- /aws/lambda/${name}
- {name: !Ref Lambda}
RetentionInDays: 1
IamRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- es:ESHttpHead
- es:DescribeElasticsearchDomain
- es:ESHttpGet
- es:DescribeElasticsearchDomainConfig
Resource: !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/es-for-lambda
PolicyName: policy
RoleName: es-proxy-lambda-role
Lambda 用 Python スクリプト
requirements.txt
以下の依存関係を追加します。なお、boto3
は Lambda ランタイム環境に事前にインストールされています。
certifi==2019.11.28
chardet==3.0.4
elasticsearch==7.5.1
idna==2.9
requests==2.23.0
requests-aws4auth==0.9
urllib3==1.25.8
lambda_function.py
以下のスクリプトは requests_aws4auth
を利用して Elasticsearch ドメインに安全に接続します。
import os
import boto3
from elasticsearch import Elasticsearch, RequestsHttpConnection
from requests_aws4auth import AWS4Auth
es_domain = os.environ.get('ES_DOMAIN')
credentials = boto3.Session().get_credentials()
awsauth = AWS4Auth(
credentials.access_key, credentials.secret_key, 'ap-northeast-1', 'es', session_token=credentials.token
)
es = Elasticsearch(
hosts=[{'host': es_domain, 'port': 443}],
http_auth=awsauth,
use_ssl=True,
verify_certs=True,
connection_class=RequestsHttpConnection
)
def lambda_handler(event, context):
response = es.info()
print(response)
samconfig.toml
<YOUR_S3_BUCKET>
を実際の値に置き換えてください。
version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "es-proxy-lambda"
s3_bucket = "<YOUR_S3_BUCKET>"
s3_prefix = "es-proxy-lambda"
region = "ap-northeast-1"
capabilities = "CAPABILITY_IAM CAPABILITY_NAMED_IAM"
デプロイとテスト
ビルドとデプロイ
以下のコマンドを使用してアプリケーションをビルドおよびデプロイします。Elasticsearch ドメインの作成には 10〜20 分程度かかります。
sam build
sam deploy
Lambda を使用したテスト
Lambda 関数を呼び出します。正常に実行されれば、関数が Elasticsearch ドメインにアクセスできることを示します。
ターミナルからのテスト
Elasticsearch ドメインへの直接アクセスをテストします。許可されていない場合はエラーメッセージが表示されるはずです。
$ curl https://<elasticsearch-domain-endpoint>/
{"Message":"User: anonymous is not authorized to perform: es:ESHttpGet"}
クリーンアップ
以下のコマンドを使用してすべてのリソースを削除します。
sam delete --stack-name es-proxy-lambda
まとめ
IAM ロール をアタッチした AWS Lambda 関数 を利用することで、Elasticsearch ドメインへのアクセスを安全に制限することができます。このアプローチにより、追加の NAT インフラストラクチャのコストを回避できます。ただし、可能であればドメインを VPC 内に配置することで、セキュリティをさらに強化することを検討してください。
Happy Coding! 🚀