Azure Container Instancesを利用し、Azure上でよくある構成をセキュアにする

クラウドチームの関戸です。 Azure Container Instancesが9/25にGAしました 。 GAの告知を見ている中で、Virtual Networkへの統合機能がPreviewされていることを知ったため、本記事ではAzure Container InstancesがVirtual Networkに統合されたことにより、Azure上のインフラセットをこれまでよりセキュアする案を思いついたため、その案の検証をしてみます。

さて、Azure上に下図のように、

よくある構成
よくある構成

  • Appサーバ
  • 踏み台サーバ

という構成を組むことは比較的多いのではないでしょうか。 Appサーバは80ポートを通してのみアクセスが可能で、解放されているポートもNetworkSecurityGroupで絞られています。 現実的には、ここにLoadBalancerなどを追加して負荷分散したりしますが、以下では話を単純にするためAppサーバと踏み台サーバに登場人物を絞ります。

このとき、踏み台として利用するサーバに対して、

  • 使うタイミングは少ないのに用意しておかなければならない
  • 使う度に踏み台を起動/終了すると待たされるのが嫌
  • 起動し続けるとして、セキュアな環境構築やID管理が手間
  • 踏み台に対する利用料金が意外とかかる

と様々な不満がありました。

踏み台用のVMを普段は落としておき、使うときだけ起動する、という運用であれば、Appサーバに対する不正なログインの懸念はかなり下がります。 ただし、さきほど挙げたように、このような運用には踏み台用VMの起動のオーバヘッドであったり、不満があった訳です。 結果的に、踏み台用VMは常時起動し続けることになったりし、せっかくのセキュアな構成なのに... と更に不満が出たりしていました。 Azure Container InstancesがVirtual Networkに統合されることによって、上記のような従来型の構成に対する不満をかなり解消できる方法がうまいことAzure上で構成可能になりました。

まず、Azure Container Instancesの(本記事における)利点は起動が高速なこと、に尽きます。 コマンド発行から数秒程度で起動します。

※現状、Virtual Networkに初回デプロイするときは、そこそこ時間がかかってしまってます(10分弱)
※デプロイ済であれば、通常通り数秒程度で起動します

Azure Container Instancesで起動するコンテナを踏み台として利用することで、Virtual Machineの起動/終了に伴う待ち時間をほぼ無くすことができます。 また、Virtual Machineのカスタマイズ(起動後にやったり、カスタムVM Imageを用意したり)は結構手間で動作確認も大変でしたが、Dockerfileの整備をすれば良くなります。 コンテナのため、ローカルでの動作確認も手軽にできます。

さて、Azure Container Instancesを踏み台として利用する場合、下図のような構成になります。

Azure Container Instancesを踏み台利用する構成
Azure Container Instancesを踏み台利用する構成

大きな違いは、踏み台関連のリソースが削除されていることと、Azure Container Instancesが追加されていることです。 他のリソースに差はないため、本筋のサービスに影響を与えずに適用可能です。

図中で、システム管理者はコンテナに対してコンテナにTTYをアタッチすると記載していますが、 こちらのドキュメントにある通り 、現状ではVirtual Networkに接続するコンテナは、同時にPublic IPを持てないためです。

Container groups deployed to a virtual network do not currently support public IP addresses or DNS name labels.

currently と書かれているため、今後に期待ですね。 そのため現状では、sshdを実行し接続することはできませんから、 docker exec 相当の az container exec を使って、リモートシェルを開くことにします。

ではこの構成での、踏み台からAppサーバへのssh接続を、 Azure CLI v2 を利用してやってみます。 サンプルリポジトリ にお試し用のスクリプトをまとめてありますので、試してみたい方はどうぞ。 なお、Azure CLI v2のバージョンは2.0.49です。

#!/bin/bash

git clone https://github.com/fixpoint/azure-container-instances-as-a-bastion
cd azure-container-instances-as-a-bastion

# お試し用のリソースを作成
# Azure Container Instancesはまだいくつかのリージョンでしか利用できないため、westusに作成する
az group create --name aci --location westus
az group deployment create --resource-group aci --template-file ./azure.template.json

# deploymentのoutputsから、Virtual Networkのリソース名を取る
vnet_name="$(az group deployment show --resource-group aci --name azure.template --output tsv --query properties.outputs.vnetName.value)"

# 踏み台用コンテナグループを作成します
# 今回は公式イメージのalpineを利用します、利用中には常駐させるため --command-line でsleepをループさせています
az container create \
    --resource-group aci \
    --name bastion \
    --image library/alpine:3.7 \
    --cpu 1 \
    --memory 0.5 \
    --vnet-name "${vnet_name}" \
    --vnet-address-prefix "10.0.0.0/16" \
    --subnet bastion-subnet \
    --subnet-address-prefix "10.0.1.0/24" \
    --command-line "/bin/sh -c 'while true; do sleep 1s; done'" \
    --restart-policy Never

# 起動したコンテナでシェルを起動します
# 現状、Windowsではaz container execコマンドはうまく動きませんので、LinuxかMacを使ってください
az container exec \
    --resource-group aci \
    --name bastion \
    --exec-command "/bin/sh"

# ここから、踏み台上での操作
# sshにalpineの環境を整えます
apk add --no-cache openssh-client
mkdir ~/.ssh
# サンプルリポジトリのprivate.pemの内容をコピーして持っていってください
vi ~/.ssh/id_rsa
chmod 0400 ~/.ssh/id_rsa

# Appサーバにsshします
# AppサーバのPrivate IPを、Azureポータルから確認しておいてください
# ここでは、10.0.0.4と仮定しています
ssh azure-user@10.0.0.4

# これでAppサーバに接続できました

# ここまで、踏み台上での操作

# 使い終わったため、コンテナを終了させる
az container stop \
    --resource-group aci \
    --name bastion

# お試し用のリソースを削除
# 現在はAzure Container InstancesをVNETに繋げた場合、削除するために一手間必要になります
# https://docs.microsoft.com/ja-jp/azure/container-instances/container-instances-vnet#clean-up-resources
network_profile_id=$(az network profile list --resource-group aci --query [0].id --output tsv)
az network profile delete --id "${network_profile_id}" -y
sal_id=$(az network vnet subnet show --resource-group aci --vnet-name "${vnet_name}" --name bastion-subnet --query id --output tsv)/providers/Microsoft.ContainerInstance/serviceAssociationLinks/default
az resource delete --ids "${sal_id}" --api-version 2018-07-01
az network vnet subnet update --resource-group aci --vnet-name "${vnet_name}" --name bastion-subnet --remove delegations 0
az group delete --name aci --yes

※手元のazがバージョン古い場合は、 docker run --rm -it -v $HOME/.azure:/root/.azure microsoft/azure-cli:latest を利用すると便利です
※Docker Public Imageを起動する場合、例えば docker run nginx:alpine としていた場合は、 az container create --image library/nginx:alpine と、 library/ を付与する必要がありました

他にも色々と利用例は考えられますが、とにかく、Azure Container InstancesからVirtual Networkに接続できるだけで、とても夢が広がるというお話は理解していただけたかと思います。 今後、Virtual NetworkにデプロイしたコンテナにPublic IPを同時に持たせることができれば、よりカジュアルにセキュアにできるのではないでしょうか。

Azure Container Instancesの日本でのGAは、2018年の終わり頃になる予定です。 待ち通しいですね。