AWS Mysql に IAM Role で接続する

MySQL 5.6 マイナーバージョン 5.6.34 以降、MySQL 5.7 マイナーバージョン 5.7.16 以降、Amazon Aurora 1.10 以降 で対応している。

ただし、1秒間あたりの接続開始の認証回数が20回と少ないため、アクセスの多いシステムでは採用できない。
IAM データベース認証の制限

Lambda をVPC内に入れてしまうと、接続まで時間がかかるため、それを回避するために使ったりする。

1. RDS で Mysql を起動

  • パブリックアクセスを有効に変更
  • SSL 証明書をダウンロードする ( rds-combined-ca-bundle.pem )
  • セキュリティーグループは、0.0.0.0/0 などに変える。

パブリックアクセスを有効にする代わりに、SSL接続することになる。

2. Mysql にユーザーを作る

RDS 作成時の Root ユーザーで、IAM 用のユーザーを作る。

プラグインを有効にしユーザーを作成

CREATE USER 'iam_user'@'%' IDENTIFIED WITH AWSAuthenticationPlugin as 'RDS';

SSL 接続を強制する

GRANT SELECT,INSERT,UPDATE,DELETE ON dbdayo.* to 'iam_user'@'%' REQUIRE SSL;

3. IAM Policy を作成

作ったポリシーは、Roleなどにアタッチする。
太字の部分は固定値です。rds-db の部分も、これで間違いなく、Webコンソール上はエラーが出るが、あなたが正しい。

arn:aws:rds-db:{RDSのリージョン}:{AWS アカウントID}:dbuser:{RDSのリソースID}/{DBのUser名}

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "rds-db:connect"
            ],
            "Resource": [
                "arn:aws:rds-db:ap-northeast-1:1234567890:dbuser:db-AAAAARRRRRR/iam_user"
            ]
        }
    ]
}

4. Lambda や EC2 にRole を関連付ける

作ったPolicyをRoleにアタッチして、さらにそのRoleをLambdaやEC2に関連付ける。

5a. EC2などからプログラムで接続

RDS_HOST = 'dbdayo.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com'
RDS_USER = 'iam_user'
RDS_REGION = 'ap-northeast-1'

def lambda_handler(event, context):
    rds = boto3.client('rds')

    password = rds.generate_db_auth_token(
        DBHostname=RDS_HOST,
        Port=3306,
        DBUsername=RDS_USER
    )
 
    conn = mysql.connector.connect(
        user=RDS_USER,
        password=password,
        host=RDS_HOST,
        database='mysql',
        charset='utf8',
        ssl_verify_cert=True,
        ssl_ca='rds-combined-ca-bundle.pem' # ダウンロードしたファイルを指定
    )

5b. EC2からコマンドで接続する

aws rds generate-db-auth-token でパスワードを生成している、認証部分がポイント

mysql -u iam_user -h dbdayo.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com \
-p`aws rds generate-db-auth-token --hostname dbdayo.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com \
--port 3306 \
--username iam_user \
--region ap-northeast-1` \
--ssl-ca=rds-combined-ca-bundle.pem \
--enable-cleartext-plugin

最近 Apple の審査でリジェクトされた時の対応方法

Guideline 2.1 - Information Needed

We have started the review of your app, but we are not able to continue because we need access to a video that demonstrates Audio on background your app in use on a physical iOS device.

Please ensure the video you provide shows a physical iOS device (not a simulator).

Next Steps

To help us proceed with the review of your app, please provide us with a link to a demo video in the App Review Information section of iTunes Connect and reply to this message in Resolution Center.

To provide a link to a demo video:

  • Log in to iTunes Connect
  • Click on "My Apps"
  • Select your app
  • Click on the app version on the left side of the screen
  • Scroll down to "App Review Information"
  • Provide demo video access details in the "Notes" section
  • Click "Save"
  • Once you've completed all changes, click the "Submit for Review" button at the top of the App Version Information page.

Once this information is available, we can continue with the review of your app.

審査の時に、テスト方法を説明する動画をアップロードする必要がある。動画をスマホで撮影して、添付しました。

Guideline 2.1 - Information Needed


We have started the review of your app, but we are not able to continue because we need additional information about your app.

Next Steps

To help us proceed with the review of your app, please provide detailed information to the following questions. The more information you can provide upfront, the sooner we can complete your review.

  • What is the significant difference between this app and your ●●● app?

Once you reply to this message in Resolution Center with the requested information, we can proceed with your review.

自分が公開している、名前が似ているアプリがある場合、違いを説明する必要がありました。実際に中身が異なっていても、名前が似ているだけで必要です。

Guideline 2.2 - Performance - Beta Testing


Your app contains references to test, trial, demo, beta, pre-release or other incomplete content.

Specifically, the naming indicates that this is beta.

“●●● β”


Next Steps

To resolve this issue, please remove all references to "demo," "trial," "beta," or "test" in your app description, app icon, screenshots, previews, release notes, and binary. If you would like to conduct a beta trial for your app, you may wish to review the TestFlight Beta Testing Guide.

アプリタイトルに β の文字がある場合は、消す必要があります。

CloudFront の CNAME は、サブドメインであれば実質2個まで同じドメインが指定可能

CloudFront の CNAME の設定は、全CloudFrontでユニークにしなければなりませんが、
CloudFront の CNAME には、ワイルドカードでも指定できるため、
*.testdomain.testwww.testdomain.test で 2つ登録すれば、実質、同じドメインが指定できます。

ただし、Route53上では、Aレコードではなく、CNAMEレコードで登録する必要があります。

これが出来れば、何か移行作業時に助かるかもしれません。

一般提供された CloudFront lambda@Edge で、Basic認証を設定する

preview と 仕様が変わっています。

nodejsは6.10を選ぶ必要があり、headers の構造も変わっていました。
Cloudfrontのarn指定に、lambda を version がなければいけないのも、なかなかめんどくさい変更です。
あとは、role の設定も面倒ですが、一旦書きません。

"use strict";

exports.handle = (event, context, callback) => {
    
    const Allows = {
        "users": "users",
    };

    const request = event.Records[0].cf.request;
    const headers = request.headers;

    const authorization = headers.authorization || headers.Authorization;

    if (authorization) {

        const enc = authorization[0].value.split(" ")[1];
        const userPassword = new Buffer(enc, 'base64').toString();

        for (let key in Allows) {
            let val = Allows[key];
            if (`${key}:${val}` === userPassword) {
                callback(null, request);
                return;
            }
        }
    }

    const response = {
        status: '401',
        statusDescription: 'Authorization Required',
        httpVersion: request.httpVersion,
        headers: {
            "www-authenticate": [{key:"WWW-Authenticate", value: 'Basic realm="Enter username and password."'}],
            "content-type": [{ key: "Content-Type", value: "text/plain; charset=utf-8" }],
        },
        body: "401 Authorization Required",
    };

    callback(null, response);
};

CloudFront にCloudFront を入れるときの注意点

CloudFront は、Request header に含まれる Hostの値が、CloudFrontに設定されたCNAMEと一致していないとレスポンスを返さない。
CloudFrontに設定されたCNAMEは、すべてのCloudFrontで2つ同じCNAMEを設定できない。

Behavior の Forward Headers は、Host を含めた場合は、ブラウザがリクエストしたHostがそのままOriginまで到達するが、
含めない場合は、Originに設定したHostのまま、Originへリクエストする。
実はここがあまり書かれていないのでわかりにくい。

この条件を満たすため、
CloufFrontのOriginにCloudFront を含めるような場合は、Behavior の Forward Headers は、All が設定できず、Whitelist を使わなければならない。
たとえば All の代わりに、User-Agent、Authorization などを設定することになるはずです。

mysql5.7 の 注意点 (< 5.7.11)

RDS 上で mysql 5.7 を使うときに覚えておかなければならないことは一つで、

default_password_lifetime
この値が、5.7.10 以下の時、360 になっています。

SELECT @@default_password_lifetime;

つまり360日で期限切れになりログインできなくなります。RDS 作成時のマスターパスワードであっても同じです。

Your password has expired. To log in you must change it using a client that supports expired passwords.

万が一、期限が切れてしまった場合は、
RDSのマスターパスワードをリセットし、mysqlにログイン。

ALTER USER user_name PASSWORD EXPIRE NEVER;

これで password_lifetime = 0 となり、ログインできます。

5.7.11以降のバージョンは、default_password_lifetime = 0 になっているので、問題はありません。

Route53 で Domain name status code がブランクな場合

jp や、io のドメインで確認しましたが、Domain name status code がブランクのままになるドメインがあります。
whois コマンドで、

[状態] Active

となっていれば、気にせず安心して良いと、AWSに回答いただきました。