しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Pipelines の Hosted Agent が持っている Outbound IP アドレスを知りたい

Azure Pipelines を使って API を呼び出す時に、たまに Outbound IP アドレスが欲しくなる時があります。今回は Firewall でアクセス元が制限されている Azure Storage への操作の時に欲しくなりました。

具体的には Azure CDN からのリクエストは許可したいが、他からのリクエストは全て拒否したいというシチュエーションです。以下は Azure Storage の Firewall 設定です。

f:id:shiba-yan:20200414001638p:plain

割と広い IP Range を登録していますが、Azure CDN の特性上仕方ないでしょう。

ちなみに https://daruyanagi.com で使っているのですが、VuePress で作成したサイトを Azure Pipelines でビルドして、Azure Storage にビルド成果物をアップロードしつつ CDN の Purge まで行っています。

Firewall 設定をしている場合に問題となるのが Azure Pipelines から Azure Storage へのデプロイです。

Azure CDN のように割り当てられている IP Range が公開されていれば楽なのですが、以下のドキュメントには Azure DevOps のリージョンと同じ IP Range を追加しろという、割と酷いことが書いてあります。

自分が使っている Azure DevOps のリージョンは East Asia ですが、大量の IP Range が登録されているのでこれを使うのは現実的な選択肢ではありません。リージョンは設定から確認できます。

f:id:shiba-yan:20200413155336p:plain

これが Azure Storage ではなく App Service なら、デプロイに利用する SCM が別エンドポイントかつ認証が必須になっているので、そもそも IP 制限を書けないという選択肢もありますが、Azure Storage の場合はそうはいきません。なので方法を考えました。

Self-hosted agent を使って Outbound IP を固定する

正直なところ正攻法は Self-hosted Agent を用意して、Outbound IP アドレス自体を固定にしてしまう方法です。Self-hosted Agent は比較的簡単に Azure にデプロイ出来ます。

Azure VM を使うなら Azure Storage に対して IP 制限を行うのではなく、Service Endpoints を利用して特定の Subnet からの通信のみ許可も出来るので、圧倒的におすすめです。

デメリットとしては VM のメンテナンスは必要になるのと、コスト面で Azure VM と Self-hosted Agent の料金が発生してきます。Private なプロジェクトでは 1 agent は無料ですが、組織単位なので複数のプロジェクトを持っていると都度追加する必要があります。

リクエストを投げて Outbound IP を調べる

今回のサイト*1 のために Azure VM を常時用意しておくなんてしたくないので、割り当てられた Hosted Agent の Outbound IP を実行時に Firewall 設定に追加してしまう方法を選びました。あまりお勧めできないですが参考までにどうぞ。

Hosted Agent は割り当ての度にマシンが変わるのと Static IP を持っていないので、ipconfigip を使って Outbound IP を取得することはできません。Stack Overflow にあるように、実際に IP アドレスを返してくれる API にリクエストを投げて確認するしかないです。

とりあえず以下の YAML を使って、それぞれの OS で Outbound IP の取得を試してみました

trigger:
- master

stages:
- stage: Windows
  jobs:
  - job: Windows
    pool:
      vmImage: 'windows-latest'
    steps:
    - powershell: Invoke-RestMethod https://ipinfo.io/json | select -exp ip

- stage: Linux
  jobs:
  - job: Linux
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: curl -s https://ipinfo.io/json | jq '.ip'

- stage: MacOS
  jobs:
  - job: MacOS
    pool:
      vmImage: 'macos-latest'
    steps:
    - script: curl -s https://ipinfo.io/json | jq '.ip'

f:id:shiba-yan:20200413203547p:plain

Windows と Linux に関しては、返ってきた IP は East Asia として公開されている Range に含まれていました。macOS に関しては Azure でホストされていないので Range は確認しようがないです。

気になるのは IP アドレスがどのタイミングで変化するかですが、ドキュメントを調べたところ送信フロー単位で IP アドレスは変わる可能性があると書かれていました。

自分が試した範囲では同一 job 内で動いてる限り、Outbound IP アドレスが変わることはなかったですが、この方法に頼りきりというのはやはりお勧めできない感じです。

今回作成した Azure Pipelines の定義は以下のようになりました。公開しているので自由にどうぞ。

出来る限り保護されたエンドポイントへのアクセスは Self-hosted Agent を使って、確実な方法でデプロイするのが理想ですが、それ以外の方法もその時の事情によっては取れるということで。

ACI でその場限りの Agent を作成するのもありでしょうが、公式に提供されない限りコスパが悪い方法だなと感じます。App Service のように ARM にデプロイ用 API があると一番楽でした。