一応できたものの、まだ全容を掴めていないのでかなりメモっぽい感じでまとめる。
やりたいこと
Azure App Service から Azure Database for PostgreSQL にパスワードレス認証したい。
現状は普通に PostgreSQL ユーザを作成してその認証情報を App Service に渡して接続しているが、これをやめたい。
ざっくり仕組み解説
- アクセス元リソース (App Service) の「マネージド ID」を発行して「サービスプリンシパル」として扱えるようにしておく
- データベース側でアクセスを許可する接続元として「サービスプリンシパル」を登録しておく
- 接続時は「マネージド ID」をもとに一時アクセストークンを入手し、それをパスワードとして使用することでデータベース側で認証できる
※ 図は Azure の公式ドキュメント から引用
※ この図はユーザプリンシパルのケースを説明しているため Step1 で az login
しているが、今回のケースではこれに相当する操作は不要
初期設定手順
Azure Portal の画面から操作した手順を記載する。
1. App Service 側でマネージド ID を有効化する
App Service
> ID
> システム割り当て済み
で オン
に設定する。
これによって App Service の「オブジェクト ID (プリンシパル ID)」が発行される。
2. データベースの認証方式として Microsoft Entra 認証を有効化する
Azure Database for PostgreSQL
> 認証
> アクセスの割り当て先
で PostgreSQL と Microsoft Entra 認証
に設定する。
この設定を変更するとデータベースサーバが再起動する。
3. App Service に紐づく (?) サービスプリンシパルを管理者として追加する
Azure Database for PostgreSQL
> 認証
> Microsoft Entra 管理者
でサービスプリンシパルを追加する。 1.
で発行されたオブジェクト ID を入力したらいけた。
4. データベース内の権限を付与
3.
までで認証が通ってデータベースに接続できるようにはなるのだが、このままだとテーブルなどの操作権限がなくて何もできない。
普通の PostgreSQL のパスワード認証で管理者ユーザで接続して、下記 SQL を実行して権限を付与する。
GRANT ALL PRIVILEGES ON DATABASE <DB_NAME> TO "<SERVICE_PRINCIPAL_NAME>";
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "<SERVICE_PRINCIPAL_NAME>";
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "<SERVICE_PRINCIPAL_NAME>";
※ この例では何でもできる権限をつけているが、必要に応じて制限した方がよさそう。
接続手順
今回の App Service では Python アプリが動いていたので、Python から接続するパターンを考える。
1. 一時アクセストークンを取得する
azure-identity
ってモジュールを pip で入れておけば、以下のノリでデータベース接続用の一時アクセストークンが得られる。
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
token = credential.get_token("https://ossrdbms-aad.database.windows.net/.default")
print(token.token)
実際は get_connection
みたいな関数に包むなどしてもうちょっといい感じに書く。
2. データベースに接続する
以下の情報を使って PostgreSQL クライアントを使って接続する。
- ユーザ名として、初期設定の際にデータベースの Microsoft Entra 管理者として追加したサービスプリンシパルの 名前 (オブジェクト ID ではない) を使用
- パスワードとして、上記
1.
で取得した一時アクセストークンを使用
注意点としては、アプリケーション起動時にトークンを取得するような作りにしてしまうと トークンの期限が切れたときに更新されず、再接続時に死ぬ ので、必ず「接続が作られるとき」にトークンを取得するようにやる。例えば SQLAlchemy であれば do_connect
イベントとか creator
, async_creator
オプションとかを使うとよい。
わかっていないこと
- この Managed Identity による方法はいわゆる「RBAC (Role Based Access Control)」とは異なる概念ってことでいいのだろうか? 今回特にロールは触ってないし...
- 今回はデータベース内の操作権限を付与するために通常の PostgreSQL ユーザを作成したが、これを回避して Microsoft Entra 認証だけで初期設定することは出来ないか?
- 今回の方法とは別に App Service の「サービスコネクタ」を作成する方法もあるようだが、何が違うのか?
参考資料
-
Active Directory 認証 - Azure Database for PostgreSQL (フレキシブル サーバー) | Microsoft Learn
- 概要図がわかりやすかった
-
Microsoft Entra ID のアプリケーションとサービス プリンシパル | Microsoft Learn
- サービスプリンシパルには「アプリケーション」と「マネージド ID」の2種類があって、今回は「マネージド ID」の方式でやった
-
Azure Database for PostgreSQL - フレキシブル サーバーでの認証に Microsoft Entra ID を使用する | Microsoft Learn
- Azure CLI (az コマンド) からアクセストークンを取得する方法が記載されている
-
Azure SDK for Python を使用して Azure リソースに対して Azure でホストされるアプリを認証する - Python on Azure | Microsoft Learn
- Azure Database ではなく Blob Storage に App Service + Python からアクセスする方法が書かれている (ほとんど一緒だが、アクセストークンを使用する箇所の手順が少し異なる)
-
マネージド ID を使用して Flask Python Web アプリを作成して Azure にデプロイする - Python on Azure | Microsoft Learn
- 今回のやり方とは別で、「サービスコネクタ」というやつを使った手順 (サービスコネクタの作成はうまく出来なくて諦めた)
-
Azure-Samples/msdocs-flask-web-app-managed-identity: Python Flask app that uses Azure managed identity.
- マネージド ID を使って Azure Database for PostgreSQL に接続する Python アプリのサンプル