LoginSignup
9
6

More than 5 years have passed since last update.

IAM認証RDS・非VPC Lambda・API Gateway・CloudFront・WAFで接続元IP制限付きAPIを作成する

Last updated at Posted at 2017-05-11

主に以下2例に対して情報を少し追加するだけの記事です。

API GatewayのバックをVPC Lambdaにしている場合、APIのくせに返答に時間がかかる場合がある、という問題があります。なので、

  • 非VPC Lambdaから安全にPublic AccessibleなRDSに接続したい
  • かつ、API Gatewayに接続元IP制限をつけたい

というのがやりたいことです。

RDS接続認証でIAMが使えるようになった

執筆時点で公式日本語ドキュメントは無し。

つまりどういうこと?

MySQLの認証プラグインを作ったからRDSイメージにいれておいたよー、という話らしいので見てみる。

select * from plugin;
+-------------------------+-------------+
| name                    | dl          |
+-------------------------+-------------+
| AWSAuthenticationPlugin | aws_auth.so |
+-------------------------+-------------+
1 row in set (0.01 sec)

同じものをPostgreSQLでも作ってくれればPostgreSQLでもIAM認証が使える用になるんですよね? (よく知らないまま希望)

それのなにがうれしいの?

MySQLのuser/password認証がAWS_ACCESS_KEY_IDとAWS_ACCESS_SECRET_KEYに置き換わったような感じです。

……って言っちゃうと「それはそう」案件になるんですが、特定のIAMロールを持つ非VPC Lambdaからのみのアクセスを許可するようなRDS MySQLを作ることができる、というのが特にうれしい点です。

MySQL組み込みの認証機構よりは比較的安全な方法で、RDSをPublic Accessibleにできるようになる、とも言います。RDSがPublic Accessibleなら、Lambdaも非VPCでよくなります。しかもそのLambdaも特定のIAMロールを持つものだけに制限できます。安心だ。

やりたいこと

RDS (IAM & SSL) | AWS Lambda (IAM) | API Gateway (https) | CloudFront (https) | Web Application Firewall

rds-iam.png

の構成を作りたい。社内APIとかですな。なんか登場人物が多いですが、以下の事情があります

  • ソースIP制限をつけたいが、API Gateway自体にソースIP制限機能がない
  • WAFにはあるが、WAFはALBかCloudFrontにしか使えない

よって、

  • CloudFrontをAPI Gatewayの前段に置く
  • WAFをCloudFrontの前段に置く

ことでアクセスをコントロールします。

API Gateway自体に関しては、API Tokenを必須にすることでアクセスを制限します。もちろんCloudFrontからはアクセスできないといけないので、Origin設定でAPI Tokenをヘッダに追加する必要があります。ややこしいなおい。

ちなみに、API Gatewayの認証をIAMにすることもできるんですが、これだとAPIユーザーにAWS v4 Signature Authを実装させることになるので心苦しいです。

うっわめんどくさっ。

やってみよう

RDSをつくる

IAM Database Authentication for MySQL and Amazon Aurora

IAM認証はdb.m1.smallより大きいインスタンスのみでしか使えないので注意。ユーザーの作り方もドキュメントのままですが、

CREATE USER jane_doe IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS' REQUIRE SSL;

DBとLambda間はSSLにしないといけないので、REQUIRE SSLオプションを付けてSSLを強制します。

Lambdaのレンジ:3306のInをAllowしたSecGroupを作る、つってもLambdaのレンジってめっちゃ広そう?
https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-ip-ranges.html

Lambdaをつくる

試しに作ったLambdaは

from __future__ import print_function

import os

import boto3
import mysql.connector

def lambda_handler(event, context):
    print(event, context)
    token = boto3.client('rds').generate_db_auth_token(
        DBHostname=os.environ['RDS_HOST'],
        Port=3306,
        DBUsername=os.environ['RDS_USER']
    )
    conn = mysql.connector.connect(
        host=os.environ['RDS_HOST'],
        user=os.environ['RDS_USER'],
        password=token,
        database=os.environ['RDS_DATABASE'],
        ssl_verify_cert=True,
        ssl_ca='rds-combined-ca-bundle.pem'
    )
    cursor = conn.cursor()
    cursor.execute('SELECT user FROM mysql.user')
    rows = cursor.fetchall()
    result = [str(x[0]) for x in rows]
    return {'users': result}

DBのユーザーをSELECTして返すだけのものです。

Lambda実行時のIAMロールにIAM Database Authポリシーが必要になります。

Attaching an IAM Policy Account to an IAM User or Role

arn:aws:rds-db:region:account-id:dbuser:dbi-resource-id/database-user-name

ここでMySQLのGRANTONTOを指定しているみたいなものと考える。

別のIAMロールを付けてLambdaを実行してみると、みごとにDB接続エラーが発生する。

Lambdaをzipでまとめる

Dockerfileを作ってzipを生成するナウでヤングな最先端のイカしたアレだぜ。デプロイ時に非VPCを選択。

FROM amazonlinux

RUN yum install -y python27-devel python27-pip zip
RUN pip install --upgrade pip
RUN mkdir /opt/rds-iam-auth /opt/build
COPY ./ /opt/rds-iam-auth/
WORKDIR /opt/rds-iam-auth
RUN pip install wheel
RUN pip install -r requirements.txt -t .
RUN zip -r rds-iam-auth.zip *

CMD cp rds-iam-auth.zip /opt/build

悲しいかなLambdaのamazonlinuxに入っているboto3がIAM RDS authに未対応 (執筆時) だったのでrequirement.txtに追記。近々AMIもアップデートされるだろう。

その他をつくる

コンソールからぽちぽちやる作業がたくさんあります。特にここで追記することはないので、先人の記事を参照したいところだ。

注意点まとめ

  • APIエンドポイントを直接使用されないように、API Tokenを必須にしておくこと。
  • WAFはGlobalリージョンで作成しないとCloudFrontのコンソールから選べないので注意。
  • CloudFrontのOrigin設定時に、x-api-tokenヘッダ転送設定を追加すること。
  • CloudFrontの設定反映には時間がかかるの辛いコーヒー飲むしかない。

ためす

WAFで社内のGlobal IPのみアクセスを許可。

$ curl https://hoge.cloudfront.net/
{"users": ["helloworld", "iamuser1", "mysql.sys", "rdsadmin"]}

WiFi変えたりとかして、Global IPを変えてみると

$ curl https://hoge.cloudfront.net/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: jIG5TSJUn9rPZYI0JwUr9wZHFHFa_LRyVmwGC302sHjBgv0sLoPubA==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>

Forbiddenされる。

まとめ

AWSよくできてんなー。

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6