Amazon Linux 2023からawscurlを使用してNeputne DBクラスターにIAMデータベース認証で接続してみた

2024.02.01

こんにちは、岩城です。

以下の公式ドキュメントを参考にNeputneのIAMデータベース認証を試す機会がありました。

実行環境として、Amazon Linux 2023を選択しました。

awscurlを導入したり、躓いたポイントもありましたので、それらを踏まえて紹介したいと思います。

IAMデータベース認証とは

NeptuneのドキュメントにIAMデータベース認証に関する詳細説明はないのですが、Auroraの公式ドキュメントには説明が記載されています。

NeptuneもAuroraもIAMデータベース認証の仕組みとしては同じだと思っているので、こちらを参考に私なりにまとめました。

  • IAMの認証トークンを利用してNeptune DBクラスターに接続でき、アプリケーションコード等でユーザーパスワードを管理する必要がない
  • 認証トークンはAWS署名バージョン4(SigV4)を使用して生成される
  • 各トークンは15分の有効期間があり、IAMを使用して外部保管され、認証情報をNeptuneに保存する必要なし
  • 仮に認証トークンが漏洩したとしても、有効期限が切れるので影響を抑えられる
  • Neptune DBクラスターとの通信はSSL/TLSを使用して暗号化される
  • 認証トークンは、各Neptune DBクラスターごとに認証情報を管理することなく、IAMを使用して外部管理されるため、一元管理できる
  • EC2で実行するアプリケーションの場合、インスタンスプロファイルを使用し、認証情報を持たせずにNeptune DBクラスターに接続できる
  • アプリケーションが1秒あたり200未満のコネクション作成に収まる場合に採用

やってみた

以下のような構成で試します。なお、各リソースの作成方法は本筋ではないので省略します。

Amazon Linux 2023にawscurlをインストールする

IAMデータベース認証が有効になっている場合、各リクエストは署名バージョン4(SigV4)を使用して署名する必要があります。

awscurlはcurlと同じ構文を使用し、SigV4署名を使用してクエリに署名できます。

awscurlがどういったものかは。以下エントリを確認すると良いです。

EC2にログインして、以下コマンドを実行しインストールします。

$ sudo dnf install -y python3 pip
$ pip install awscurl

Neptune接続用IAMロールをassume roleし一時的な認証情報を取得する

以下コマンドを実行しNeptune DBクラスター接続用のIAMロールをassume-roleします。ちなみに引き受け先のIAMロールにはNeptuneReadOnlyAccessポリシーを付与しています。

OUTPUT=`aws sts assume-role --duration-seconds 1800 --role-arn "<引き受け先のIAMロールのARN>" --role-session-name AWSCLI-Session`
AWS_ACCESS_KEY_ID=`echo $OUTPUT | jq -r .Credentials.AccessKeyId`
AWS_SECRET_ACCESS_KEY=`echo $OUTPUT | jq -r .Credentials.SecretAccessKey`
AWS_SESSION_TOKEN=`echo $OUTPUT | jq -r .Credentials.SessionToken`
export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
export AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN

コマンドは公式ドキュメントに記載されているものを参考にしました。

公式ドキュメントに記載のコマンドをコピペすると以下のような問題があり、私の環境ではそのまま利用できませんでした。

  • -bash: $output: ambiguous redirectが出力する
  • The AWS Access Key Id you provided does not exist in our records.が出力する

これらの問題を回避するため、上のようなコマンドとしました。横道に逸れますが、躓きポイントなので各問題の原因について説明しておきます。

「-bash: $output: ambiguous redirect」が出力する

output変数を事前定義していないのにリダイレクト先と指定しているためです。

なので、以下のようにリダイレクトせずに直接OUTPUTに代入しました。

OUTPUT=`aws sts assume-role --duration-seconds 1800 --role-arn "<引き受け先のIAMロールのARN>" --role-session-name AWSCLI-Session`

「The AWS Access Key Id you provided does not exist in our records.」が出力する

私が激ハマりしたところです。

結論から言いますと、以下コマンドだとjqコマンドで抽出した各種文字列にダブルクォーテーションが含まれた環境変数に代入されるためです。

AWS_ACCESS_KEY_ID=`echo $OUTPUT | jq .Credentials.AccessKeyId`
AWS_SECRET_ACCESS_KEY=`echo $OUTPUT | jq .Credentials.SecretAccessKey`
AWS_SESSION_TOKEN=`echo $OUTPUT | jq .Credentials.SessionToken`

分かってしまえば大したことは無いのですが、当時はダブルクォテーションを考慮から外して文字列が一致しているか確認していたので、本当に頭を悩ませました。

# 誤
$ env |grep AWS_
AWS_SECRET_ACCESS_KEY="<シークレットアクセスキー>"
AWS_ACCESS_KEY_ID="<アクセスキー>"
AWS_SESSION_TOKEN="<セキュリティトークン>"

# 正
AWS_SECRET_ACCESS_KEY=<シークレットアクセスキー>
AWS_ACCESS_KEY_ID=<アクセスキー>
AWS_SESSION_TOKEN=<セキュリティトークン>

ということで、jqコマンドに-rオプションを付与しRAWデータを出力するように変更しました。これでダブルクォテーションを含まなくなります。

AWS_ACCESS_KEY_ID=`echo $OUTPUT | jq -r .Credentials.AccessKeyId`
AWS_SECRET_ACCESS_KEY=`echo $OUTPUT | jq -r .Credentials.SecretAccessKey`
AWS_SESSION_TOKEN=`echo $OUTPUT | jq -r .Credentials.SessionToken`

awscurlを使用してNeptune DBクラスターに接続する

それではIAMデータベース認証でNeptune DBクラスターに接続します。

接続するNeptune DBクラスターでIAMデータベース認証が有効である前提です。

公式ドキュメントを参考に、以下のコマンドを実行します。

ドキュメントにはNeptune DBクラスターのエンドポイントを指定するとありますが、httpsから始めるようにしてください。

$ awscurl https://devio-dev-neptune-cluster.cluster-c4o0khmattoq.ap-northeast-1.neptune.amazonaws.com:8182/status \
    --region ap-northeast-1 \
    --service neptune-db
{"status":"healthy","startTime":"Wed Jan 31 03:26:26 UTC 2024","dbEngineVersion":"1.3.0.0.R1","role":"writer","dfeQueryEngine":"viaQueryHint","gremlin":{"version":"tinkerpop-3.6.4"},"sparql":{"version":"sparql-1.1"},"opencypher":{"version":"Neptune-9.0.20190305-1.0"},"labMode":{"ObjectIndex":"disabled","ReadWriteConflictDetection":"enabled"},"features":{"SlowQueryLogs":"disabled","ResultCache":{"status":"disabled"},"IAMAuthentication":"enabled","Streams":"disabled","AuditLog":"enabled"},"settings":{"clusterQueryTimeoutInMs":"120000","SlowQueryLogsThreshold":"5000"}}'

jqコマンドで整形してみました。Neptune DBクラスターに接続され、クラスターのステータスを確認できたことが分かると思います。

$ echo '{"status":"healthy","startTime":"Wed Jan 31 03:26:26 UTC 2024","dbEngineVersion":"1.3.0.0.R1","role":"writer","dfeQueryEngine":"viaQueryHint","gremlin":{"version":"tinkerpop-3.6.4"},"sparql":{"version":"sparql-1.1"},"opencypher":{"version":"Neptune-9.0.20190305-1.0"},"labMode":{"ObjectIndex":"disabled","ReadWriteConflictDetection":"enabled"},"features":{"SlowQueryLogs":"disabled","ResultCache":{"status":"disabled"},"IAMAuthentication":"enabled","Streams":"disabled","AuditLog":"enabled"},"settings":{"clusterQueryTimeoutInMs":"120000","SlowQueryLogsThreshold":"5000"}}' | jq .
{
  "status": "healthy",
  "startTime": "Wed Jan 31 03:26:26 UTC 2024",
  "dbEngineVersion": "1.3.0.0.R1",
  "role": "writer",
  "dfeQueryEngine": "viaQueryHint",
  "gremlin": {
    "version": "tinkerpop-3.6.4"
  },
  "sparql": {
    "version": "sparql-1.1"
  },
  "opencypher": {
    "version": "Neptune-9.0.20190305-1.0"
  },
  "labMode": {
    "ObjectIndex": "disabled",
    "ReadWriteConflictDetection": "enabled"
  },
  "features": {
    "SlowQueryLogs": "disabled",
    "ResultCache": {
      "status": "disabled"
    },
    "IAMAuthentication": "enabled",
    "Streams": "disabled",
    "AuditLog": "enabled"
  },
  "settings": {
    "clusterQueryTimeoutInMs": "120000",
    "SlowQueryLogsThreshold": "5000"
  }
}

おわりに

IAMデータベース認証は、Neptune接続時に利用するユーザー/パスワードを保持する必要がなく、よりセキュアにNeptuneを利用できます。

簡単に利用できるものではないと想像できますが、そういった機能もあると覚えていただければと思います。

本エントリがどなかたのお役に立てれば幸いです。