Azure環境において最小権限でストレージ統合を構成する

Azure側を最小権限で構成する方法が書かれた記事がありそうで全然見つからない。
そこで、本記事ではAzure側を最小権限で構成する方法を調べてまとめてみた。

2023年7月現在、公式はSASを使う方法ではなくストレージ統合を使う方法を推奨している。
ストレージ統合によりAzureADへ認証を委譲することで認証情報を持ち回る必要がなくなる。
本記事ではストレージ統合を使用する。

ストレージアカウントにはURL(認証)・N/Wの2系統のパブリックアクセスの経路が存在するが、
URL(認証)によるパブリックアクセスは遮断し、Snowflake VNetからのアクセスのみ許可する。
Azure Portal/CLIからのアクセスを固定IPで制限する。

ストレージアカウント

ストレージアカウントは、Blob(Object)、Files(SMB)、Queue、Table(NoSQL)の4種類が存在する。
Snowflakeの外部ステージとして使用するのはAWS S3でありAzure Blobである。
Blobは最上位階層にない。ストレージアカウント内にはコンテナがあり、コンテナ内にBlobがある。

Blobストレージ内のオブジェクトに対してのみ、外部からアクセス(パブリックアクセス)できる。
パブリックアクセスには、URL・ネットワークの2経路が存在し、それぞれ独立して存在する。
他方を制限したからといってもう一方が自動的に制限されたりはしない。

パブリックURLアクセス

ストレージアカウントレベルで、パブリックアクセスを以下から選択できる。

  • Enabled
  • Disabled

また、コンテナレベルで、パブリックアクセスを以下から選択できる。

  • Private (no anonymous access)
  • Blob (anonymous read access for blobs only)
  • Container (anonymous read access for containers and blobs)

上位階層であるストレージアカウントレベルの設定が優先される。上位で不許可にすれば不許可。
もしストレージアカウントレベルで許可した場合、コンテナレベルの設定が効く。
その組み合わせは以下の通り。確かにMECEなのだが絶望的に冗長に思える。
コンテナ自体はURLアクセス不許可だがBlobはURLアクセス可とか設定できてしまう。

パブリックネットワークアクセス

パブリックネットワークアクセスとして以下から選択できる。

  • Enabled from all network
  • Enabled from selected virtual networks and IP addresses
  • Disabled

分かりづらいが、ストレージアカウント/Blobへのアクセスに使われるN/Wインターフェースを選ぶ。
Disabledにすると、仮想ネットワークのプライベートIPアドレスを使用するN/Wインターフェースとなる。
Enabledには2つあり、全てのN/Wからアクセス可能なN/Wインターフェースか、
選択した仮想N/WまたはIPアドレスからアクセス可能なN/Wインターフェースを選ぶ

もちろんDisabledにした場合は、仮想プライベートエンドポイントが必要となる。

ストレージ統合の際のストレージアカウントの最小権限構成

以下のようにして最小権限構成を行なった。

  • パブリックURLアクセスをストレージアカウントレベルでDisabled
  • パブリックネットワークアクセスを固定IP、及びSnowflake VNet IDsに設定
  • Snowflakeのストレージ統合機能によりサービスプリンシパルを作成
  • ストレージコンテナのIAMでサービスプリンシパルにBlob読み書きロール設定

ストレージアカウントに対するSnowflakeのVNetサブネットIDsからのアクセス許可

ストレージアカウントへのパブリックアクセスを「選択したVNetまたはIPアドレスのみ」とした場合、
SnowflakeのVNetまたはIPアドレスが分かる必要がある。IPアドレスは不定なのでVNet IDsを指定する。
公式にドンピシャの説明があるので、それを参考にVNet IDsを設定する。
VNet サブネット IDs の許可

Snowflake側で以下のSQLを実行すると、属するVNet IDがJSONで複数出力される。
それを記録しておく。


USE ROLE ACCOUNTADMIN;
SELECT SYSTEM$GET_SNOWFLAKE_PLATFORM_INFO()
;
{
  "snowflake-vnet-subnet-id":[
    "/subscriptions/hogehoge",
    "/subscriptions/fugafuga"
  ]
}

続いて、ストレージアカウントに上の2個のVNet IDからのアクセスを許可する。
以下のように、Azure CLIでログインしてVNet IDの個数だけコマンドを実行する。


$ az storage account network-rule add --account-name  \
                                      --resource-group  \
                                      --subnet "/subscriptions/hogehoge"
...
$ az storage account network-rule add --account-name  \
                                      --resource-group  \
                                      --subnet "/subscriptions/fugafuga"
...

ストレージ統合を作成する

AWS環境と同様に、CREATE STORAGE INTEGRATION によりストレージ統合を作成する。
Azure CLIやAzure PortalでテナントIDを取得しておいてAZURE_TENANT_IDに渡す。
STORAGE_ALLOWED_LOCATIONSにはBLOBの位置を表す文字列を渡す。
書式は azure://ストレージアカウント名.blob.core.windows.net/コンテナ名/パス 。
BLOBの位置は複数渡せる。


CREATE STORAGE INTEGRATION storage_integration_azure
  TYPE = EXTERNAL_STAGE
  STORAGE_PROVIDER = 'AZURE'
  ENABLED = TRUE
  AZURE_TENANT_ID = ''
  STORAGE_ALLOWED_LOCATIONS = ('azure://storageikuty1.blob.core.windows.net/container1/files/')
;

続いて、同意URL(AZURE_CONSENT_URL)を取得する。


DESC STORAGE INTEGRATION storage_integration_azure
;
property	property_type	property_value	property_default
ENABLED	Boolean	true	false
STORAGE_PROVIDER	String	AZURE
STORAGE_ALLOWED_LOCATIONS	List	azure://storageikuty1.blob.core.windows.net/container1/files/	[]
STORAGE_BLOCKED_LOCATIONS	List		[]
AZURE_TENANT_ID	String	<テナントID>
AZURE_CONSENT_URL	String	https://login.microsoftonline.com/hogehoge&response_type=code
AZURE_MULTI_TENANT_APP_NAME	String	<マルチテナントアプリ名>
COMMENT	String

URLを開き、表示された画面で「承認」ボタンを押下する。
この際、権限がなければ権限不足である旨表示されてここで終了する。(権限は後述)
このタイミングでAzure環境にSnowflakeと対応するサービスプリンシパルが作られる。
上手くいくとSnowflakeの企業サイトに遷移する。

Azure Portalにログインし、Snowflakeと共有したいストレージアカウントを選択する。
IAMから、サービスプリンシパルに対して以下のロールを割り当てる。
サービスプリンシパルIDは、上で得られたマルチテナントアプリ名のハイフンの前の部分。
この辺り、むちゃくちゃ煩雑で、AWSよりも大変。

  • Storage Blob Data Reader(ストレージBlobデータ閲覧者)
  • Storage Blob Data Contributor(ストレージBlobデータ共同作成者)

次にDBにフォーマットと(外部)ステージを作成する。


USE ROLE SYSADMIN;
USE DATABASE HOGE;

CREATE OR REPLACE FILE FORMAT MY_CSV_FORMAT
    TYPE = CSV
    COMMENT = '接続検証'
;

CREATE OR REPLACE STAGE my_azure_stage
  STORAGE_INTEGRATION = storage_integration_azure
  URL = 'azure://storageikuty1.blob.core.windows.net/container1/files/'
  FILE_FORMAT = my_csv_format;
;

該当のBlobにファイルをアップロードする。(パスを指定した場合忘れがち)
そして疎通確認。


$ show storage_integration_azure
...
$ list @storage_integration_azure
...

まとめ

Azure環境でストレージ統合によりBlobアクセスを設定してみた。
その際、Azure環境側でパブリックアクセスの開き方が最小限になるように構成した。
Azure側、Snowflake側共に大きな権限が必要だが、公式推奨だし可能ならこの方法が良さそう。