はじめに
SAMを利用してPrivate APIを作成する良いサンプルがなかったので作りました。
※誤りあればご指摘いただけると幸いです。
前提
・aws-cli、sam-cliがインストール済みであること。
・credentialが設定済みであること。(aws configure
が便利)
・SAM用のS3バケットが用意されていること。
サンブルコード
template.yamlのみ確認した場合はこちら↓
サンプルコードの使い方
1 2 3 4 5 6 7 8 9 10 11 |
# サンプルを取得 git clone https://github.com/sun-bs/apigateway-lambda-private-api-sample.git # template.yaml内のVpcIdとSubnetIdを書き換える vim template.yaml # Lambdaをビルドする sam build # スタックをデプロイする(SAM用のS3バケット名を指定してください) sam deploy --stack-name private-api --s3-bucket [S3バケット名] --region ap-northeast-1 --capabilities CAPABILITY_NAMED_IAM --template .aws-sam/build/template.yaml |
curlサンプル(動作確認)
apigatewayのIDを置換して、同じVPC内でcurlコマンドを発行してください。
1 2 |
curl https://[your apigateway id].execute-api.ap-northeast-1.amazonaws.com/api/private-api-endpoint/dummy-get curl -d dummy-post https://[your apigateway id].execute-api.ap-northeast-1.amazonaws.com/api/private-api-endpoint |
Q & A
Q. Private APIにカスタムドメインを指定できないか。
A. 指定不可です。API Gatewayにカスタムドメインを設定する機能はありますが、privateでは使用できません。
route53への設定を考える方もいらっしゃるかもしれませんが、
・割り当て先であるPrivate Host Zoneに対するAliasレコードは指定不可です。
・内部のネットワークのみで名前解決される前提でCNAMEで指定したとしてもAPI GatewayのTSL証明書記載のドメインとアクセス先ドメインが異なるため、HTTPSの接続時にクライアント側でエラーとなります。
Q. RegionalでPrivateっぽく使えないか。
A. 不可です。VPCのリソースではないため、インターネット経由のアクセスとなってしまいPrivateなネットワークを経由しません。
また、VPC EndpointのポリシーでIP制限は可能ですが、アクセス元VPCやVPCエンドポイントのアクセス制限は不可です。
一応、VPC EndpointのポリシーでNAT GatewayのIPアドレスを指定することで擬似的にPrivateにすることはできます。
Q. open API部分を別ファイルに切り出せないか。
A. DifinitionUriで別ファイルにすることは可能ですが、cloudformationの関数が使用不可となるため不便です。
また、Fn::TransformしたものをDifinitionBodyへ指定することも可能ですが、S3へのファイルアップロードが必要となるため億劫です。
template.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Mappings: Constant: ResourceId: VpcId: vpc-xxxxxxxxxxxxxxxxx # your VPC ID SubnetId: subnet-xxxxxxxxxxxxxxxxx # your subnet ID Resources: ###################################################################### # Lambda # ###################################################################### # Lambda PrivateApiFunction: Type: AWS::Serverless::Function Properties: CodeUri: lambda_src/ Handler: app.lambda_handler Runtime: python3.9 FunctionName: private-api Role: !GetAtt PrivateApiRole.Arn Events: GetEndpoint: Type: Api Properties: Path: /private-api-endpoint/{arg} Method: get RestApiId: !Ref PrivateApiApiGateway PostEndpoint: Type: Api Properties: Path: /private-api-endpoint Method: post RestApiId: !Ref PrivateApiApiGateway # Lambda実行ロール PrivateApiRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: 'sts:AssumeRole' RoleName: private-api-lambda-role ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole ###################################################################### # API Gateway # ###################################################################### # Api GatewayへのLambdaアクセス許可 PrivateApiFunctionPermission: Type: AWS::Lambda::Permission Properties: FunctionName: !GetAtt PrivateApiFunction.Arn Action: lambda:InvokeFunction Principal: apigateway.amazonaws.com SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${PrivateApiApiGateway}/*' # API Gateway PrivateApiApiGateway: Type: AWS::Serverless::Api Properties: Name: private-api StageName: api MethodSettings: # ログを有効化 - DataTraceEnabled: true LoggingLevel: 'INFO' ResourcePath: '/*' HttpMethod: '*' EndpointConfiguration: Type: PRIVATE VPCEndpointIds: - !Ref PrivateApiVpcEndpoint DefinitionBody: openapi: 3.0.3 info: title: private-api description: private-api schemes: - https consumes: - application/json produces: - application/json paths: /private-api-endpoint/{arg}: get: # API GatewayがLambdaを呼び出す設定 x-amazon-apigateway-integration: contentHandling: "CONVERT_TO_TEXT" uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PrivateApiFunction.Arn}/invocations" passthroughBehavior: when_no_templates payloadFormatVersion: "1.0" connectionType: "INTERNET" httpMethod: POST type: aws_proxy /private-api-endpoint: post: # API GatewayがLambdaを呼び出す設定 x-amazon-apigateway-integration: contentHandling: "CONVERT_TO_TEXT" uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PrivateApiFunction.Arn}/invocations" passthroughBehavior: when_no_templates payloadFormatVersion: "1.0" connectionType: "INTERNET" httpMethod: POST type: aws_proxy # VPCエンドポイントからのリクエストのみに制限する。 x-amazon-apigateway-policy: Version: '2012-10-17' Statement: - Effect: Allow Principal: '*' Action: 'execute-api:Invoke' Resource: - !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*' Condition: StringEquals: aws:SourceVpce: !Ref PrivateApiVpcEndpoint ###################################################################### # API Gateway共通設定 # ###################################################################### # API Gatewayのログ出力用ロール ApiGatewayCloudWatchRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - apigateway.amazonaws.com Action: 'sts:AssumeRole' Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs # API Gatewayにログ出力用ロールを設定 ApiGatewayAccount: Type: 'AWS::ApiGateway::Account' Properties: CloudWatchRoleArn: !GetAtt ApiGatewayCloudWatchRole.Arn ###################################################################### # VPCエンドポイント # ###################################################################### # VPCエンドポイント PrivateApiVpcEndpoint: Type: AWS::EC2::VPCEndpoint Properties: ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api SubnetIds: - !FindInMap [Constant, ResourceId, SubnetId] VpcId: !FindInMap [Constant, ResourceId, VpcId] VpcEndpointType: Interface SecurityGroupIds: - !GetAtt PrivateApiSg.GroupId PrivateDnsEnabled: true # VPCエンドポイント用セキュリティグループ PrivateApiSg: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: PrivateApiSg GroupName: PrivateApiSg VpcId: !FindInMap [Constant, ResourceId, VpcId] SecurityGroupIngress: - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 |