Google Cloud のイケてる(ような気がする)サービス・機能のご紹介
この記事は「Google Cloud Japan Customer Engineer Advent Calendar 2020」の 10 日目の記事です。(2日ほど遅れていますが…)
こんにちは、Customer Engineer (ネットワーク)の有賀と申します。Customer Engineer といいつつ「(ネットワーク)」とあるように、ネットワークサービスしか分かりません。
そこで、普段あまり関わらないけど、イケてる(ような気がする) Google Cloud のサービス・機能をとりとめなくご紹介したいと思います。(といいつつ、普段関わってるサービスも入ってますが…)
本記事の内容はすべて執筆時(2020年12月10日)の情報にもとづいていることにご注意ください。
お品書き
(モバイルからだとリンクに飛べないかもしれません 🙏 )
Cloud Asset Inventory
Cloud Asset Inventory はプロジェクトをまたがって Google Cloud で利用されてるアセット、リソースを表示、モニタリング、分析できるメタデータ インベントリ サービスです。
クラウドサービスを使ってると、いろんなプロジェクト(アカウント)のいろんな場所(リージョン)にいろんなサービス(リソース)を簡単に作れるので動作確認や「ちょっと試してみよう」みたいなノリで使っていると、特に検証環境や開発環境だと思わぬところに思わぬリソースができちゃったりして、それで費用が発生してしまったりすることがあると思います。
さらに、費用が発生すれば存在を認識できるのでまだいいですが、費用が発生しないリソースだとひっそりとクラウドの片隅でそのまま永らえたりすることもあります。(セキュリティ的に問題なければ特に気にしなくてもいいかもしれませんが、統制(コンプライアンス)的な観点からはあまりよろしくありません。)
そこで Cloud Asset Inventory です!Cloud Asset Inventory では次のようなことができます。
- アセットのスナップショットを取得 (
gcloud asset list
) (PREVIEW) - アセットのスナップショットを BigQuery や Google Cloud Storage へエクスポートする (
gcloud asset export
) - リソースや IAM ポリシーを検索 (
gcloud asset search-all-resources
、gcloud asset search-all-iam-policies
) - アセットの履歴(作成・削除・更新)を取得 (
gcloud asset get-history
) - アセットの変更を監視 (
gcloud asset feeds
) - IAM ポリシーを分析 (
gcloud asset analyze-iam-policy
)
Cloud Asset Inventory では VM などのリソース (
resource
)や、IAM ポリシー (iam-policy
)、組織ポリシー (org-policy
)、Access Context Manager のポリシー (access-policy
)などのポリシー、VM の OS インベントリ(os-inventory
)などを総称して「アセット」と呼んでいます。
アセットのスナップショットを取得 (PREVIEW)
論より証拠、こんな感じに使えます。(“Organization” は「組織」と呼ばれる、Google Cloud のリソース階層の一番上(ルート)に位置するリソースです。)
Cloud Asset Inventory ではサポートされているリソースを、プロジェクト、リージョンをまたがってすべて列挙してくれます。ただ、たとえばアセットのタイプ(例: --asset-types=''
)とかでフィルタしないと膨大な情報が出力され非常に時間がかかる場合があります。その場合はエクスポート(gcloud asset export
)の利用をご検討ください。
また、リソースや IAM ポリシーをフィルタを指定して検索することもできます。ただ、検索できるリソースタイプが、サポートされている全体のリソースタイプより少ないことや、そもそも gcloud
コマンド全般と同じく出力フォーマットを JSON にできるため、jq
コマンド等に慣れてる方は、エクスポートした JSON ファイルを整形、フィルタした方が自由度は高いかもしれません。
jq
を使って、たとえば全プロジェクトの全世界にあるインスタンスの各ゾーンごとの台数とかも簡単に集計できます。
各サブネットでどれくらい IP アドレスが使用されてるかを調べる方法をしばしば質問されるのですが、こんな感じで数えることもできます。
ちなみにサポートされてるリソースのリストを見ると、ロードバランサーのリソースが無いように一瞬思われるかもしれませんが、Google Cloud ではロードバランサーのリソースは Compute Engine のカテゴリに含まれています。(例: compute.googleapis.com/UrlMap )
アセットの履歴を取得
アセットのスナップショットを取得するだけでなく、指定した期間に指定したアセットに対して発生した、作成・変更・削除などの履歴を取得することもできます。
次の例はinstance-1
、instance-2
というインスタンスのステータスの推移を表示したものです。作成から削除までの推移を見て取ることができます。
アセットの変更を監視
これだけアセットの情報をとれるなら、受動的に情報を取得するだけでなく、何か変更があった場合に能動的に通知して欲しいと思うのは自然な発想です。
Cloud Asset Inventory では当然そのような機能に対応しています。😁
gcloud asset feeds create
でフィードを作ると、対象として指定したアセットの状態に変化があるたびに Cloud Pub/Sub にメッセージが送られるようになります。
具体的には以下のような感じです。初めに Pub/Sub のトピックを作っておいてから、たとえば Compute Engine のインスタンスを対象としたフィードを送るようにします。
そしてインスタンスを作成した後、作成したトピックからメッセージを読んでみると、インスタンスの作成時のステータスが変化するたびにメッセージが Pub/Sub へと送られていたことが分かります。
この監視はリソースの変更だけでなく、IAM ポリシー、組織ポリシー、アクセスポリシーなどに対しても実施できます。たとえば以下の例では、 constraints/compute.disableSerialPortAccess
という組織ポリシー(VM のシリアルポートアクセスを使わせない制約です)を無効化した際の変更を受け取っています。このように、誤って(もしくは故意に)重要な組織ポリシーを変更された場合でも、すぐにその変更の通知を受けることが可能になります。(極端な話、Pub/Sub から Cloud Functions を呼び出して変更を元に戻しちゃうなんてことも可能です)
なお、監視の対象はアセットタイプだけでなく、アセット名での指定や、細かい条件を付けて絞り込むことも可能です。
IAM ポリシーを分析
最後の IAM ポリシーの分析は、同じ Cloud Asset Inventory の機能でも他の機能とは少し毛色が違います。この機能を使うと、たとえば、あるインスタンスをスタート(compute.instances.start
)できる権限をもったロール(と、そのロールが付与されてるユーザー)の一覧とかを簡単に取得できます。
予期しない権限を付与してしまっていないかや、逆に権限が足りない場合などの調査に役立てていただけます。いろいろと参考になるサンプルもそろってますので、ぜひお試しください。
(お品書きに戻る)
VM マネージャー
Cloud Asset Inventory のアセットの一つとして OS インベントリ(os-inventory
)がありました。OS インベントリ管理は時々目にはするものの触ったことがなかったので調べてみました。
まず、VM マネージャーという機能(サービス)が Google Compute Engine で動く OS Config エージェント(デフォルトでは無効)を使ってサービスを実現しています。そして、この VM マネージャーは以下の 3 つの機能を提供しています。
- OS インベントリ管理
- OS パッチ管理
- OS 設定管理 (PREVIEW)
実現できることは、それぞれの機能の名前から想像できる通りの内容ですね。ドキュメントの絵をそのまま拝借するとこんな感じです。
というわけで、サクッと OS Config エージェントを有効にしたインスタンスを作って試してみます。
OS Config エージェントを有効化するには enable-osconfig=TRUE
という設定をプロジェクトのメタデータに設定する方法と、インスタンスのメタデータに設定する方法がありますが、テストなので後者で試します。
Google Cloud が提供している VM イメージには OS Config エージェントがすでにインストールされてるものも多く、その場合はインスタンスを起動するだけで使えます。(個別にインストールすることももちろんできます)
なお、OS Config エージェントの中身が気になる方は、こちらのリポジトリをご覧ください。
OS インベントリ管理
OS インベントリ管理では OS Config エージェントが10分毎にインベントリ管理に必要な情報をスキャンしメタデータサーバーへ送ってます。
取得してる情報は大きく分けて、オペレーティングシステムの情報(Linux だとこんな感じ)とパッケージの情報(こんな感じ)になります。
実行結果はこんな感じで、見やすいといえば見やすいですね。(もちろん JSON や YAML でもとれます)
プロジェクトメタデータ、インスタンスメタデータに
enable-os-inventory=TRUE
を設定してください。
当然ですが Cloud Asset Inventory でも同じ情報を取得できます。(併用すれば、たとえばパッケージのインストール履歴とかもとれますね)
OS パッチ管理
OS パッチ管理はgcloud compute os-config patch-jobs execute
のように実行し、インスタンスのそれぞれの環境のパッケージマネージャを使ってアップデート(パッチ当て)を実施できます。
簡単な実行例はこんな感じです。
対象とするインスタンスを指定したり(除外したり)、対象とする(しない)パッケージを指定したり、パッチ後の再起動の扱いを指定したり、apt を使ってる場合はデフォルトで apt upgrade
なのを apt dist-upgrade
に変更したり、適用する Windows Update のカテゴリを指定したり、等々、いくつか細かい設定をすることができます。
OS 設定管理 (PREVIEW)
OS 設定管理では guest policy
というもので必要・不要なパッケージを指定したり、レポジトリを変更したり、パッケージになってないソフトウェアのインストールなどを指示したりできます。OS Config エージェントはこの guest policy
にしたがって、各インスタンスの設定をおこないます。
たとえば、必須のツール tcpdump
をインストールさせたいと思ったら、こんな感じで実現できます。
単にパッケージをインストールする以外の設定例もいくつかありますので、ぜひお試しください。
(お品書きに戻る)
外部アドレス無しインスタンスへのアクセス
OS Config エージェントの例のところでさりげなく使ったんですが、Google Cloud では外部 IP アドレスを持たないインスタンスへのアクセスもとても簡単です。
インスタンス作成時に --no-address
を指定し外部アドレスをつけないようにし、アクセスする際は gcloud compute ssh
でアクセスすれば、外部 IP アドレスが無いことを自動的に認識して、IAP トンネリング (Identity-Aware Proxy)を経由してアクセスします。
SSH の接続元は 35.235.240.241
になっていますが、これは IAP のアドレスブロック(35.235.240.0/20
)に含まれています。(逆に言えば、このブロックからの接続をファイアウォール等でブロックすれば IAP 経由の接続は不可となります)
ブラウザからも SSH で接続できますが、その時も IAP が使われます。
さて、これはどのような仕組みで接続してるのでしょう。再度ドキュメントの絵を拝借するとこんな感じです。
要は、IAP というプロキシとクライアント(gcloud コマンドやブラウザ)が HTTPS でつながり、その中を SSH を通してるってことのように見えます。では、実際にどんな風に動いてるのか、gcloud コマンドの方を見てみましょう。
gcloud コマンドを --verbosity=debug
をつけて実行すると裏で何をやってるか分かるので、抜粋しながら見てみたいと思います。(見やすいように改行をずらしてます)
これを見ると、ポイントはssh
コマンドのプロキシとして python3 -S gcloud.py compute start-iap-tunnel no-external-addr %p --listen-on-stdin --project=... --zone=... --verbosity=...
を指定しているところのようです。
gcloud コマンドのマニュアルを見ると --listen-on-stdin
が載ってないので、これは gcloud compute ssh
用にだけ使ってるようです。当たり前ですが、手動で実行すれば普通に動きます。
$ gcloud compute start-iap-tunnel no-external-addr 22 \
> --listen-on-stdin
SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2
通常の使い方だと、下の例のようにローカルポートフォワードみたいに使うのですが、上の例だと標準入力(stdin)につながるところが特別です。
$ gcloud compute start-iap-tunnel no-external-addr 22
Picking local unused port [20796].
Testing if tunnel connection works.
Listening on port [20796].
(待ち状態)# 別ターミナルで実行
$ ssh -i .ssh/google_compute_engine -p 20796 agira@127.0.0.1
Linux no-external-addr 4.19.0-12-cloud-amd64 #1 SMP Debian 4.19.152-1 (2020-10-18) x86_64
(以下略)
これは SSH の ProxyCommand が The command can be basically anything, and should read from its standard input and write to its standard output.
(SSH_CONFIG(5)より)という仕様だからです。
図にするとこんな感じになります。
では、gcloud compute start-iap-tunnel
は具体的に何をしてるのでしょうか。上の出力を見ると Connecting with URL ['wss://tunnel.cloudproxy.app/v4/connect?project=...
とあるので、WebSocket で tunnel.cloudproxy.app
というホストに接続しているようです。(FAQ にその旨の記述があります)
ちょっとだけコードを見てみると InitiateConnection()
→ _startNewWebSocket()
→ IapTunnelWebSocketHelper()
→ websocket.WebSocketApp()
みたいな流れで WebSocket の接続を確立しているようでした。(ホスト名とかはこちらで設定されてます)
gcloud コマンドと IAP の間で認証がおこなわれた後、HTTPS(とWebSocket)による暗号通信(トンネル)が張られ、その中を SSH の通信が通ることで、外部 IP アドレスを持たないインスタンスにも接続ができていることが分かりました。
と、ここまで分かれば gcloud コマンドを使わなくても SSH の設定だけで外部 IP アドレスを持たないインスタンスに接続できそうです。( gcloud compute ssh
コマンド自体は必要な引数を設定して SSH を実行してるだけなので)
他にもいくつか設定はありますが、とりあえず最小の設定だとこんな感じで接続できました。
なお、接続先のホスト名 no-external-addr
は ProxyCommand の中で指定してるので、ssh コマンドの引数の接続先ホスト名( compute.123
)はダミーとなります。
また、上記オプションを同じように ~/.ssh/config ファイルに設定として書いてあげれば、オプション無しで接続できるようになります。
Host compute.123
IdentityFile ~/.ssh/google_compute_engine
ProxyCommand gcloud compute start-iap-tunnel no-external-addr %p --listen-on-stdin
(もしアカウントを切り替えて使ってるような場合は ProxyCommand
と gcloud
の間に env CLOUDSDK_CORE_ACCOUNT=user@example.com
のように使いたいアカウントを追記すればオッケーです。)
ここまで設定しておくと、たとえば Visual Studio Code の SSH 接続などを使って接続し、リモートのファイルを手元で編集しながらの開発も簡単に実現できます。
(お品書きに戻る)
組織ポリシー
外部との通信の制限
そもそも外部 IP アドレス無しインスタンスが使われるのはどんな場面でしょうか。よくあるのは、会社やプロジェクトのポリシーで、外部との通信が制限されてる場合などです。ファイアウォールなどでブロックするのも効果的ですが、外部 IP アドレスがついてなければ(IAP経由での通信は例外として)通信すらできません。
そこで、ルールとして「外部 IP アドレスをインスタンスにつけないように!」としても中々徹底するのは難しいです。
そこで出てくるのが組織ポリシーです!
Cloud Asset Inventory の項にも出てきましたが、組織ポリシーを定義することで、組織全体、あるフォルダ配下、特定のプロジェクトなどの粒度で、すべてのリソースに制限をかけることができます。
たとえば上記の「外部 IP アドレスを付けない」というのは constraints/compute.vmExternalIpAccess
という制限で実現できます。実行結果は以下の通り。
この例では asia-northeast1-b ゾーンの bastion
というインスタンスにだけ外部 IP アドレスの利用を許可しています。
なお、組織ポリシーは設定した後の操作にしか影響しません。つまり、たとえば
constraints/compute.vmExternalIpAccess
を設定しても、それまでに外部 IP アドレスがついてる VM には影響しません。
さて上に「外部 IP アドレスがついてなければ(IAP経由での通信は例外として)通信すらできません」と書きましたが、実は外部 IP アドレスがついてなくても外部から通信できる方法が他にもあります。まず、外部のネットワークとつながる Cloud Interconnect、Cloud VPN そして VPC ピアリングを使う方法。そして、ロードバランサーのバックエンドとして設定する方法やプロトコルフォワーディングを使う方法もあります。さらに Cloud NAT を使えば外部への通信もできてしまいます。
大丈夫です!😎 これらの抜け道も以下の組織ポリシーを使うことでふさぐことができます。
constraints/compute.restrictDedicatedInterconnectUsage
(Interconnect)constraints/compute.restrictPartnerInterconnectUsage
(Interconnect)constraints/compute.restrictVpnPeerIPs
(VPN)constraints/compute.restrictVpcPeering
(VPC Peering)constraints/compute.restrictLoadBalancerCreationForTypes
(LB)constraints/compute.disableInternetNetworkEndpointGroup
(LB)constraints/compute.restrictProtocolForwardingCreationForTypes
constraints/compute.restrictCloudNATUsage
(NAT)
リソースの場所の制限
また、外部との通信の制御と同じようによくお問い合わせをいただくのが、リージョンの使用の制限になります。特に日本でのクラウドの利用を考えた時、東京リージョンと大阪リージョンが使えれば十分なケースが多く、逆に他のリージョンに誤ってリソースを作ってしまうのを防ぎたい、というご要望があります。
上でご紹介した Cloud Asset Inventory で監視し、都度対応することもできますが、もう少し積極的に、特定のリージョンの利用を強制(他のリージョンの使用を制限)することはできないのでしょうか?
大丈夫です!😎 constraints/gcp.resourceLocations
という組織ポリシーで利用するリージョンを制限することができます。具体的な使い方はこんな感じです。
asia1
は東京リージョンと大阪リージョンのセットを指しており、Cloud Spanner のマルチリージョン構成や Google Cloud Storage のバケットの指定で使えます。
テストが終わったら組織ポリシーを削除しておきます。
$ gcloud resource-manager org-policies delete \
> constraints/gcp.resourceLocations \
> --project=project-ongcp-app
Deleted [<Empty>].
なお、この組織ポリシーについては以下の点に注意してください。
- この組織ポリシーに対応しているサービスのリストはこちらです。
- 複数のサービス、API が協調して動作するような場合、この組織ポリシーによって意図せぬ挙動となる場合があります。開発環境等での事前の十分な検証をオススメします。
- リソースの作成場所(リージョン)の制限となりますので、そもそもグローバルなリソース(リージョンを指定しないようなリソース)には影響がありません。
- リソースが作成されるリージョンと、そのリソースがデータをどこに置くか、は必ずしも一致しない場合があります。(そのため、データの保管場所の制限には厳密には使えない場合があります。)
このように、組織ポリシーを使うことで組織、フォルダー、プロジェクトなどの単位で簡単に全体に制限をかけることができ、積極的な統制を実現することができます。組織ポリシーによってまるっと制限をかけた上で、個別の要件には IAM ポリシーやファイアウォール、アクセスポリシーなどで細かくご対応いただくのがオススメです。
(お品書きに戻る)
階層型ファイアウォールポリシー (PREVIEW)
組織ポリシーでは組織、フォルダーなどの単位で、全体に一括して制限をかけられることをご紹介しましたが、同じように全体にかけられたらいいのになぁ、と誰しも一度は思ったことがあるに違いないものがあります。
そうです、ファイアウォールポリシーです。
クラウドサービス、特に IaaS (Infrastructure as a Service)を使っている場合には必ず設定が必要になるファイアウォール。そもそも細かいポリシーに従ってルール自体をたくさん作らないといけない上に、プロジェクトや VPC が増えたら同じルールを何度も設定しなければならず、これは DRY (Don’t Repeat Yourself)の原則に反してると言わざるを得ません。😖
そこで階層型ファイアウォールポリシーです。階層型ファイアウォールポリシーではファイアウォールルールを組織内のプロジェクト全体を対象とした設定や、特定のフォルダ配下のプロジェクト全体を対象とした設定をおこなうことができます。
この図のように、各インスタンスには、組織全体のファイアウォールポリシー、フォルダ単位のファイアウォールポリシー、そして VPC に設定されたファイアウォールポリシーがそれぞれ適用されることになります。
具体的な例を一つ見てみましょう。以下のように組織階層、フォルダ “A部” 階層、そして VPC 階層にそれぞれファイアウォールが設定されているとします。
このような構成では、VM1、VM2 には結果的にそれぞれ以下のようなルールが適用されることになります。
- VM1: 1.1.1.0/24 からの HTTP/HTTPS のみ許可
- VM2: 1.1.1.0/24 からの HTTP/HTTPS/SSH のみ許可
VM1 は VPC ファイアウォールですべて不許可(deny all)となってますが、フォルダ階層で HTTP/HTTPS が許可されており、そちらが優先されます。ただし、組織階層での 1.1.1.0/24 からの通信のみの許可との論理積となります。
VM2 はどこからでも HTTP/HTTPS/SSH を許可するようになってますが、組織階層で 1.1.1.0/24 からの通信のみ許可されてるので、その論理積となります。
なお、上の例の組織階層のポリシーは goto_next
となっているのでルールのルールの評価は次に進みますが、フォルダ階層のルールは allow
となっているのでルールの評価はそこで完了します。
階層型ファイアウォールポリシーを使えない環境では、組織レベル、フォルダレベルで設定されているファイアウォールポリシー(上図の緑の四角)を、”vpc1" と “vpc2” それぞれにコピーして設定しなければならず、その後の運用の際の変更漏れなどにつながりやすくなります。
では、実際に設定をしてみて、本当に上記の通りになっているか確認してみます。テストに使う環境は下図のような簡単な階層構造となっており、各プロジェクトではウェブサーバーが動いてます。
まず初めに組織レベルのファイアウォールポリシー org-level-security-policy
を作成します。上の例で 1.1.1.0/24
となっているところは 13.113.159.175/32
にしてます。
次にフォルダレベルのファイアウォールポリシー folder1-security-policy
を作成します。なお、この時点では組織レベル、フォルダレベルのいずれのファイアウォールポリシーも有効になってはいません。
最後に各 VPC に通常のファイアウォールルールを設定します。
さてこの状態( VPC ファイアウォールだけが有効な状態)で一旦アクセスしてみます。なお、gcloud beta compute instances network-interfaces get-effective-firewalls
で有効になっているファイアウォールルールを表示できるので、その結果も合わせて見ていきます。
以下 allowed-host
が組織レベルのファイアウォールポリシーで設定した13.113.159.175
のホストで、denied-host
がその他のホストです。
階層型ファイアウォールポリシーはまだ関係なく、普通の VPC ファイアウォールに従ってるので、すべて DENY
となってる service1
にはアクセスできず、HTTP が全開になってる service2
へはどちらからもアクセスできています。
では次に組織レベルのファイアウォールポリシーを有効にします。有効化に当たっては、ファイアウォールポリシーを各階層に関連付け(associations create
)します。
get-effective-firewalls
の結果を見ると org-firewall
というのが増えてるのが見て取れます。そして denied-host
から service2
へのアクセスができなくなりました。これは org-firewall の二行目(Priority 9999)で不許可になったためです。
一方、allowed-host
から service1
へのアクセスは依然できていません。これは、org-firewall のアクションが goto_next
であり、次の VPC ファイアウォールでブロックされてしまっているからです。
では最後にフォルダレベルのファイアウォールポリシーを有効にします。
org-firewall がさらに増え、新しいセキュリティポリシーIDのファイアウォールポリシーが増えています。
フォルダレベルのセキュリティポリシーを有効にしたことで、ようやく allowed-host
から service1
へアクセスできるようになりました。 service1
側の get-effective-firewalls
の結果を見ると、三行目の ID 1066706068557
のセキュリティポリシーによって HTTP への通信が許可(allow
)されるようになったためです。(なお、リストにはポート番号等の条件は表示されていません)
というわけで、上に書いた通りなっていることをご確認いただけたかと思います。
- service1: 13.113.159.175/32 からの HTTP/HTTPS のみ許可
- service2: 13.113.159.175/32 からの HTTP/HTTPS/SSH のみ許可
最後に get-effective-firewalls
で取得できる情報は Cloud Console でもこんな風に見られますので、合わせてご紹介いたします。
(お品書きに戻る)
非常に長くなってしまいましたが、これにてようやく終了です。他にも色々といい感じの機能があるのですが、それついてはまた別の機会に書きたいと思います。
次のエントリ(というか本記事が二日遅れてしまったので公開のタイミングは前ですが…)は Yuichiro Danno さんによる「メディアにオススメ!Google 印の Transcoder API 爆誕!」です。ぜひご覧ください。