外部IPを持たないCloud SQLにIAPを使ってローカルマシンからアクセスする

2023-12-07

久しぶりの投稿となります。
エヌデーデーの関口です。
ずいぶん前になってしまいますが、WebPerformer関連で新機能の紹介を兼ねた実装方法などをいくつか紹介していました。(ご興味があれば、検索してみてください)

今回は、WebPerformerネタではなくGoogle Cloudの記事です。いずれWebPerformerをGoogle Cloud上で動作させるネタを書くつもりなので、その下準備だと思ってください。

下準備とは言っても、しっかりGoogle Cloudの事を書くのでご安心ください。

想定される読者

  • Google Cloudを利用している
  • Cloud SQLを利用している
  • ローカルの開発環境を整えたい

なお、なぜWebPerformerの記事を書いていた人間が、Google Cloudなのか?という点については、私自身がGoogle Cloudの全資格を保有しており、Google Cloud公式のエンタープライズユーザー会(Jagu’e’r)にも所属して活動しているくらい好きという理由も多分に影響しております。(Jagu’e’rでの私の活動もご興味あれば、検索してみてください)

今回解決したいシステム構成

今回のタイトルのような課題の紹介でよく見かけるのは、次のような構成です。Cloud SQLに外部IPがあり、そこに対してCloud SQL Auth Proxy(後述)を使って接続するケースですね。

クリックで拡大

一方今回解決したいシステム構成ではCloud SQLに外部IPがないため、上記のような構成が取れません。これを解決したいという記事です。

クリックで拡大

プライベートサービス接続を有効にしたVPCを作成する

今回想定しているCloud SQLは外部IPを持ちません。外部IPを持たないということは、ローカルマシンはもちろんCloud Shellからも接続して、SQLを発行する事ができません。

そこで、Cloud SQLへは信頼できるネットワークからのみのアクセスを受け付けるように設定し、そのネットワーク上の踏み台のマシン(GCE)からはコマンドを受け付けるように、プライベートサービス接続の設定を行います。

次に示す手順は、gcloudコマンドやTerraformでおこなえますが、ここではわかりやすくコンソールからやってみましょう。

VPCを作成する

Google Cloudのプロジェクトを選択した状態でVPCネットワークを開きます。

検索窓下の[VPCネットワークを作成]を押して新規ネットワークを作成します。サブネット作成モードはカスタムにしましょう。

クリックで拡大

続くサブネットの項目は次のように設定します。
メインのIPv4範囲の他に、セカンダリIPv4範囲を設定していますが、今回の記事では使いませんので設定していなくても大丈夫です。

また、重要なのは図の一番下にある限定公開のGoogleアクセスをオンにしておくことです。これによってGoogleのサービスに内部IPで通信ができるようになります。

クリックで拡大

プライベートサービス接続を設定する

作成したVPCをリストから選択して[プライベート サービス接続]タブを選択し、さらに[サービスに割り当てられた IP 範囲]のタブを選択します。
表示されている[割り当て IP 範囲]ボタンをクリックします。

内部IP範囲の割り振りというダイアログが出てくるので、名前や説明を適宜入力します。
今回はIP範囲を自動で選択することにしますが、カスタムを指定してCIDRを指定することもできます。
画面の注意書きにもありますが、Googleのサービスと接続する場合にはプリフィックスは24を指定します。

クリックで拡大

内部IPの範囲が作成できたら[サービスへのプライベート接続]タブを開き、[接続を作成]をクリックします。
ダイアログが開くので接続サービスプロデューサーからGoogle Cloud Platformを選択して、割り当てから先ほど作成した内部IPの範囲を選択します。

クリックで拡大

これにより、Google Cloud が提供する各種サービスとの間にVPCネットワークピアリングが確立されます。
実際にVPCの[VPCネットワーク ピアリング]というタブを見てみるとservicenetworking-googleapis-comという名称のピアリングができているはずです。

ファイアウォール ルールを追加する

次に、後ほど作成する踏み台マシン(GCE)に安全にSSH接続できるように準備しておきます。GCEへの安全な接続方法として推奨されるのは、IAP(Internet-Aware Proxy)を使用する方法です。これを使えばGoogleアカウントを利用して、かつ内部IPしかもたない仮想マシンへの接続も容易になります。

IAPを利用してGCEにSSH接続できるようにするためには、ファイアウォール ルールで 35.235.240.0/20 という送信元から利用するポート(今回であればSSHの22ポート)の受信を許可する必要があります。

先ほど作成したVPCをリストから選択し、下部タブのファイアウォールに移動して[ファイアウォール ルールを追加]を押します。

トラフィックは上りを許可して、踏み台マシンへのIAP通信のみを許可するために今回の場合はiapという名前のターゲットタグを設定しました。

クリックで拡大

重要なポイントは送信元IPv4範囲ですね。こちらを35.235.240.0/20に設定します。
また、プロトコルとポートについてはSSHを許可するためTCPの22番を許可します。

クリックで拡大

これでVPCの準備はできました。

Cloud SQLの環境を用意する

次に外部IPを持たないCloud SQLを用意します。

Google Cloud ConsoleからCloud SQLを作成する

Cloud SQLのメニューから[インスタンスを作成]を押します。今回はPostgreSQL 14を選択してみます。
最近リリースされたEnterprise Plusが選択できるようになっているみたいですね。

クリックで拡大

本稿では開発環境想定で、可用性やマシンスペックはスモール構成で進めます。

接続の設定ではプライベートIPにチェックを入れ、パブリックIPからはチェックを外してください。
また、プライベートIPにチェックを入れたときに表示される関連付けられたネットワーキングでは先に作成していたVPCネットワークを選択します。

正しくプライベートサービス接続の設定ができていれば、グリーンのチェックアイコンが表示されるはずです。

クリックで拡大

設定を終えたら[インスタンスを作成]をクリックしましょう。作成完了までにはしばらく時間が掛かります。

Cloud SQLへの接続を確認する

Cloud SQLのインスタンスができたら、接続確認を行います。
ただし、プライベートIPしか持たないCloud SQLには、ローカルマシンからはもちろん、Cloud Shellからも接続ができません。

そこで、次に踏み台マシンを作成し、そこから接続します。踏み台マシンをプライベートサービス接続が有効になっているVPCネットワーク上に作ることで接続ができるという訳です。

踏み台マシンを用意する

さて、踏み台マシンを用意しましょう。

インスタンスの作成

VMインスタンスのメニューから[インスタンスを作成]をクリックします。
リージョンはVPCで作成したサブネットと同じリージョンを選択してください。マシンタイプは最小でもかまわないのでe2-microにしました。

クリックで拡大

また、[アイデンティティとAPIへのアクセス]ではアクセススコープで"すべての Cloud API に完全アクセス権を許可"を選択します。

クリックで拡大

[詳細オプション]を拡げて、さらに[ネットワーキング]も拡げます。ネットワークタグには、先ほどのファイアウォール ルールで設定したiapを設定します。
そして、ネットワークインターフェースの編集では、先ほど作成したVPCネットワークとそのサブネットを選択します。

クリックで拡大

最後にIPアドレスの設定です。外部IPv4アドレスを"なし"に設定します。

クリックで拡大

すべて設定を終えたら作成しましょう。

作成完了を待って、インスタンスのリストから[SSH]を押してログインできることを確認してください。

Cloud NATを設定する(オプション)

実はこのままの踏み台マシンの設定だと少々不便です。それは外部IPがないためインターネットへの接続ができず、ツールのダウンロードなどが手間になってしまうのです。
試しにapt-getしてみましょう…エラーになりますね。

そこで、外部IPを持たないインスタンスでもインターネットにアクセスできるようにするために、Cloud NATを設定します。(踏み台マシンで利用するツールをアップロードしてインストールする方法の場合は設定は不要です)

コンソールからCloud NATのメニューに移動して[開始]を押します。

ゲートウェイの名前を指定し、Cloud Routerの選択に移ります。
ネットワークでは既に作成したVPCを指定し、サブネットのあるリージョンも指定しましょう。

次にCloud Routerを選択しますが、現時点ではCloud Routerがないのでドロップダウンから[新しいルーターを作成]をクリックすると、画面右に入力項目が表示されるので名前を入力して作成します。(他の項目はデフォルトのままでよいです)

Cloud Routerを作成するとドロップダウンで選択できるようになり下図のようになります。
Cloud Router以下の項目はデフォルトのままでいいので、そのまま作成してください。

クリックで拡大

さて、もう一度踏み台マシンのターミナルからapt-getしてみましょう…処理が成功しましたね!

ツールのインストール

踏み台マシンからCloud SQLに接続を確認するために、PostgreSQLのクライアントツールpsqlをいれます。

sudo apt-get update
sudo apt-get install postgresql-client

インストールが終わったら、次のコマンドを入力して接続確認してみましょう。
Cloud SQLのプライベートIPアドレスは、Cloud SQLのインスタンス一覧で確認できます。
正しくパスワードが入力され、postgres=> という文字が出ていれば接続成功です。

psql -h {Cloud SQLのプライベートIPアドレス} -U postgres -d postgres
Password for user postgres: {パスワード}

接続確認ついでに、テーブルを作ってデータを入れておきましょう。

CREATE TABLE users(
  user_id VARCHAR(10) PRIMARY KEY,
  user_name VARCHAR(20) NOT NULL,
  password VARCHAR(50) NOT NULL
);

INSERT INTO users VALUES ('user1','USER 001','password1');
INSERT INTO users VALUES ('user2','USER 002','password2');
INSERT INTO users VALUES ('user3','USER 003','password3');

SELECT * FROM users;
 user_id | user_name | password  
---------+-----------+-----------
 user1   | USER 001  | password1
 user2   | USER 002  | password2
 user3   | USER 003  | password3
(3 rows)

もう一つツールを入れておきます。Cloud SQL Auth Proxyというツールで、このツールが実行中の環境(マシン/サービス)から、Googleアカウントやサービスアカウントの権限を参照して、Cloud SQLへの通信をプロキシすることができるツールです。つまり、認証されたという情報がないとCloud SQLへの通信を許可しないということです。
次のコマンドでツールをダウンロードし、実行権限を付与します。

curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.6.0/cloud-sql-proxy.linux.amd64
chmod +x cloud-sql-proxy
sudo mv ./cloud-sql-proxy /usr/local/bin/

試しに踏み台マシンから接続をしてみましょう。
SSHのターミナルで次のコマンドを実行します。実行すると待ち受け状態になるので、もう一つSSHのターミナルを開いてください。

cloud-sql-proxy --port 5432 --private-ip {インスタンス接続名}

2つ目のターミナルで次のコマンドを入力してみてください。
宛先がlocalhostになっていることに注目してください。Cloud SQL Auth Proxyが起動中はlocalhostへのpsqlの通信がCloud SQLインスタンスへ転送されるのです。

psql -h localhost -U postgres -d postgres
Password for user postgres: {パスワード}

接続できたら SELECT * FROM users; で先ほどのデータが確認できるはずです。

おや?認証された情報がないのに、どこで認証の情報を提供してるの?と思われたかも知れません。

実は踏み台マシンに付いているデフォルトのサービスアカウントは、非常に多くのGoogle Cloudのサービスにアクセスできる権限を持っています。そして作成する時に“すべての Cloud API に完全アクセス権を許可"という設定を行っていたのですが、これはスコープと呼ばれるもので、権限とは別にAPIにアクセス許可の範囲を示しています。この二つの組み合わせで、特に権限を指定しなくてもCloud SQLにアクセスできたという訳です。

実際の運用ではサービスアカウントを作成し、適切なロール(roles/cloudsql.clientなど)を付与してから、その認証キーファイルを作成しておき、次のようにCloud SQL Auth Proxyの起動時に設定してあげるというのが良い方法です。

cloud-sql-proxy --port 5432 --private-ip --credentials-file {サービスアカウントの認証キーファイルの場所} {インスタンス接続名}

踏み台マシンからpsqlを使えれば充分じゃないかという意見もあるかと思いますが、Cloud SQL Auth Proxyを入れる事で、ローカルマシンから外部IPを持たないCloud SQLに対しても、みなさんがよく利用されるデータベース接続ソフト(A5Mk2など)を利用することが可能になるので、開発の時には便利という訳ですね。

最後にマシン起動時にCloud SQL Auth Proxyが起動できるように設定しておきましょう。

コンソールから踏み台マシンを選択して[編集]を押します。スクロール下部の[管理]セクションにある[自動化]に次のように書いて保存します。次回再起動後にはCloud SQL Auth Proxyがサービスとして起動しています。
--address 0.0.0.0というパラメータで、この踏み台マシンに入っていくる通信をすべて待ち受ける事になります。

cloud-sql-proxy --address 0.0.0.0 --port 5432 --private-ip {インスタンス接続名}

これで、ひとまずCloud SQLにつないで開発できる環境は整いました。

ローカルマシンからつないでみる

踏み台マシンからpsqlでCloud SQLにはつなぐことができました。では、次はローカルマシンからアクセスしてみます。ローカルマシンにはCloud SDKがインストールされているものとします。

ファイアウォールの設定変更

IAPで踏み台マシンにSSHできるようにするために、ファイアウォール ルールで35.235.240.0/20からの22番ポートを許可しました。同じようにローカルからIAPを経由して踏み台マシン上のCloud SQL Auth Proxyが待ち受けている、PostgreSQLのポート5432番を許可します。

ファイアウォール ポリシーのメニューに移動して、既に作成しているIAPを許可するルールを選択します。
[編集]を押して、プロトコルとポートの項目に既に入っている22番に加え5432番を加えて保存します。

ローカルマシンからIAPでトンネリングする

ファイアウォールで5432番ポートを開けたので、次のコマンドを実行することでローカルマシンの5432ポートを踏み台マシンの5432ポートにトンネリングしてくれるようになり、最終的には踏み台マシンの5432ポートへの通信はCloud SQLへ転送される事になります。

Listening on port [5432].と出たら成功です。PowerShellのターミナルはそのままにしておきましょう。

gcloud compute start-iap-tunnel {踏み台マシン名} 5432 --local-host-port=localhost:5432 `
--zone={踏み台マシンのゾーン}

Testing if tunnel connection works.
Listening on port [5432].

この状態で今回はA5Mk2を使ってCloud SQLにつないでみます。

接続設定はlocalhostのPostgreSQLに接続する時と同じ設定を入れているだけです。
(ローカルマシンでPostgreSQLが動作している場合は停止しておいてください)

クリックで拡大

さあ、接続してみましょう。動画を撮りましたのでご覧ください。
最初は接続が拒否されてエラーになりますが、コマンドを入力すると接続できるようになったのが確認できます。

クリックして拡大

最終的なシステム構成は次のような感じになりました。

まとめ

今回は、外部IPを持たないCloud SQLにローカルマシンから接続する方法をご紹介しました。踏み台マシンを用意しなければなりませんが、セキュアな接続を担保しつつローカルマシンでの開発もスムーズに行えるので、試してみてください。