💨

FlowiseAI を Cloud Run でセキュアに動かす

2023/09/21に公開

FlowiseAI を Cloud Run でセキュアに動かす

本記事では、 GUI でカスタム LLM フローを簡単に作成することのできる Flowise の概要に加えて、Cloud Run 上で セキュアに動かす 方法を紹介していきます。

Flowise とは

Flowise とは、GUI でカスタム LLM フローを作るためのツールです。
Flowise v1.3.3 からVertex AI の PaLM API for Text / Chat, Embeddings などに対応しています。
https://twitter.com/FlowiseAI/status/1689664739045986304

シンプルな LLM Chainから、QnA Retrieval Chain といった Vector store を使う少し複雑なものまで、GUI で作成することができます。
Simple LLM chain
シンプルな LLM chain

また、作成したカスタム LLM フローは API として利用することができ、利用する際に必要なコードも以下のように出力できます。
API usage
作成した LLM フローを API として利用する方法

Flowise には、Marketplace によくある LLM フローのパターンがテンプレートとしてまとまっています。まずはこれをベースに、簡単なカスタムフローを試してみるのが良いでしょう。
Marketplace
Flowise の Marketplace

なお、Vertex AI PaLM API を Flowise で検証する方法については、 Beatrust Yongtae san のブログで紹介されています。是非、読んでみてください。
https://tech.beatrust.com/entry/2023/08/22/Google_Cloud_PaLM_2をFlowiseで検証_%3A_直感を活かすNo_codeでの検証方法

Getting Started

それでは、早速 Flowise を動かしていきましょう。

ローカルで動かす

以下のコマンドだけで簡単に動かすことができます。

npm install -g flowise
npx flowise start

Flowise の Vertex AI への認証方法は、三通りあります。

  1. "Connect credential" 内に認証情報のパスを指定する
  2. "Connect credential" 内に認証情報を記述(コピペ)する
  3. Application Default Credentials を利用する

1, 2 の場合は、"Connect credential" にサービスアカウントの認証情報を指定することができるので、 credential ごとに異なる権限管理をすることができます。
3 の場合は、自身の Google アカウントの権限に則ってアクセスが制御されます。

以下は、3 を選択した場合の設定方法です。
Flowise の設定の中で credentials を指定していないことが分かります(クライアントライブラリがローカル認証情報ファイルを参照します)。

Authorization
Vertex AI の認証

なお、ローカル認証情報ファイルは、以下のコマンドを実行することで保存されます。
自身の Google Account が Vertex AI への適切な権限 を持っているかどうかを確認しておきましょう。

gcloud auth application-default login

Cloud Run で簡単に動かす

では、次は Cloud Run で動かしてみましょう。
以下の手順に従って進めていくと、Flowise を Cloud Run で動かすことができます(冒頭の Project ID などは適宜変更してください)。

なお、本手順は 簡単に動かす ための手順で、 セキュアに動かす ための手順は本記事の後半で解説しています。是非、最後までお読みください :)

まず、コマンド実行時の変数を設定します。

PROJECT_ID=sample-project           # 変更してください
FLOWISE_USERNAME=admin
FLOWISE_PASSWORD="skxZm&SE2Wa35W0E" # 変更してください
REGION=asia-northeast1
ZONE=asia-northeast1-a
DATABASE_INSTANCE=flowise-instance
DATABASE_USER=postgres
DATABASE_PASSWORD="Aqt9Db64w8"      # 変更してください

次に、今回利用する API を有効化します。

gcloud services enable \
    servicenetworking.googleapis.com cloudbuild.googleapis.com \
    compute.googleapis.com artifactregistry.googleapis.com \
    sql-component.googleapis.com run.googleapis.com \
    aiplatform.googleapis.com secretmanager.googleapis.com \
    --project=${PROJECT_ID}

これで準備が整いました。
まず、Cloud Build で Flowise のコンテナイメージをビルドします。
なお、最後の builds submit コマンドで実行されるビルドには 15 - 20 分程度の時間がかかります。ゆっくり待っても良いですし、 --async オプションを付けて、次の DB instance に進んでも良いでしょう :)

# Get flowise code
git clone https://github.com/FlowiseAI/Flowise.git
cd FlowiseAI

# Create a repository in Artifact Repository
gcloud artifacts repositories create flowiseai --location=${REGION} \
    --repository-format=docker --project=${PROJECT_ID}

# Build a container image of Flowise
gcloud builds submit \
    --tag=${REGION}-docker.pkg.dev/${PROJECT_ID}/flowiseai/flowiseai . --project=${PROJECT_ID}

# (optional) Build a container image of Flowise with '--async' option
gcloud builds submit \
    --tag=${REGION}-docker.pkg.dev/${PROJECT_ID}/flowiseai/flowiseai . --project=${PROJECT_ID} --async

次にデータベースを準備します。Flowise では、デフォルトだと SQLite のデータベースが使われますが、今回は Cloud Run のスケールも考慮して Cloud SQL を使います。
(Cloud Run のスケールアウトに加えてリクエストがない場合は 0 インスタンスまでスケールインするのと、インスタンスが何らかの原因で再作成される可能性もあるので、データを永続化するために外部の DB を使うのが良いでしょう)
https://docs.flowiseai.com/databases

# Configure VPC peering
gcloud compute addresses create google-managed-services-default \
    --global \
    --purpose=VPC_PEERING \
    --prefix-length=16 \
    --network=projects/${PROJECT_ID}/global/networks/default --project=${PROJECT_ID}
gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=google-managed-services-default \
    --network=default \
    --project=${PROJECT_ID}

# Create Cloud SQL for PostgreSQL instance
gcloud sql instances create ${DATABASE_INSTANCE} \
    --database-version=POSTGRES_15 \
    --tier db-custom-1-3840 \
    --network default --no-assign-ip \
    --zone=${ZONE} --project=${PROJECT_ID}

# Set password
gcloud sql users set-password ${DATABASE_USER} \
    --instance=${DATABASE_INSTANCE} \
    --password=${DATABASE_PASSWORD} --project=${PROJECT_ID}

# Create database
gcloud sql databases create flowise \
    --instance=${DATABASE_INSTANCE} --project=${PROJECT_ID}

ちなみに、データベースは以下の構造になっています。
chat_flow が フロー定義、 chat_message が prompt と output を保持しており、画面上でクリアするとメッセージ履歴を chat_message から削除することができます。

psql "sslmode=disable dbname=flowise user=postgres hostaddr=172.26.0.3"

flowise=> \dt
            List of relations
 Schema |     Name     | Type  |  Owner   
--------+--------------+-------+----------
 public | chat_flow    | table | postgres
 public | chat_message | table | postgres
 public | credential   | table | postgres
 public | tool         | table | postgres
(4 rows)

最後に、Cloud Run をデプロイします。
Cloud Run からデータベースに接続する際、 private ip で接続するためには、 VPC に接続する必要があります。今回は Direct VPC egress という機能を使っていますが、2023/09 時点ではプレビューのため、本番利用する場合は Serverless VPC Access を検討しましょう。

DATABASE_HOST=172.26.0.3 # 変更してください

# Deploy Flowise service to Cloud Run
gcloud beta run deploy flowise --port 3000 --region ${REGION} \
    --image ${REGION}-docker.pkg.dev/${PROJECT_ID}/flowiseai/flowiseai \
    --set-env-vars "FLOWISE_USERNAME=${FLOWISE_USERNAME},FLOWISE_PASSWORD=${FLOWISE_PASSWORD},DATABASE_TYPE=postgres,DATABASE_PORT=5432,DATABASE_HOST=${DATABASE_HOST},DATABASE_NAME=flowise,DATABASE_USER=${DATABASE_USER},DATABASE_PASSWORD=${DATABASE_PASSWORD}" \
    --network=default --subnet=default --vpc-egress=private-ranges-only --project=${PROJECT_ID} \
    --allow-unauthenticated # Public access

Flowise のセキュリティ

2023/09 現在、Flowise は 以下の 2つの認証に対応 しています。

  1. App level
    • アプリケーションへのログインを ID / Password で保護
  2. Chatflow level
    • 定義したカスタム LLM フローを API として利用する際、API key で保護

これだけだと少し心もとない方もいるかと思いますので、ここからは、Google Cloud の各種機能を使って、Flowise へのアクセスのセキュリティを高めていきましょう。

Cloud Run でセキュアに動かす

さて、ここからが本記事の本題です。最初に、今回の全体像を示します。

Architecture
Google Cloud 上で Flowise をセキュアに動かす

先程は Flowise を Cloud Run で動かしているというだけでしたが、ここから以下の設定を追加していきます。

  1. Service Account による適切な権限制御
  2. Secret Manager の利用
  3. Cloud Run への ingress を制限
  4. Cloud Armor による IP 制限 と IAP による Google アカウント認証

なお、Cloud Run のセキュリティについてもっと知りたい場合は、Google Cloud 内製化 Day - 実践!Cloud Run セキュリティの動画を御覧ください。資料(PDF) も公開されています。

1. Service Account による適切な権限制御

Cloud Run では、特に指定しない場合は Compute Engine のデフォルトサービスアカウントが設定されます。
https://cloud.google.com/run/docs/configuring/service-accounts

最小権限の原則 に則り、Compute Engine のデフォルトサービスアカウントを利用するのではなく、Cloud Run のサービス個別に必要な権限を与えるのが良いでしょう。

まず、サービスアカウントを作成します。

# Create Service Account
gcloud iam service-accounts create sa-flowise \
    --description="Service account for Flowise" \
    --display-name="Flowise on Cloud Run service account" --project=${PROJECT_ID}

# Add permission to access Vertex AI PaLM API
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:sa-flowise@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/aiplatform.user" --project=${PROJECT_ID}

次に、作成したサービスアカウントを使うように Cloud Run のリビジョンを構成しましょう。

# Delete Cloud Run service
gcloud run services delete flowise --project=${PROJECT_ID}

# Update Cloud Run service to use the service acount
gcloud beta run deploy flowise --port 3000 --region ${REGION} \
    --image ${REGION}-docker.pkg.dev/${PROJECT_ID}/flowiseai/flowiseai \
    --set-env-vars "FLOWISE_USERNAME=${FLOWISE_USERNAME},FLOWISE_PASSWORD=${FLOWISE_PASSWORD},DATABASE_TYPE=postgres,DATABASE_PORT=5432,DATABASE_HOST=${DATABASE_HOST},DATABASE_NAME=flowise,DATABASE_USER=${DATABASE_USER},DATABASE_PASSWORD=${DATABASE_PASSWORD}" \
    --network=default --subnet=default --vpc-egress=private-ranges-only --project=${PROJECT_ID} \
    --allow-unauthenticated \
    --service-account sa-flowise@${PROJECT_ID}.iam.gserviceaccount.com # added

上の例では、Vertex AI User (roles/aiplatform.user) をサービスアカウントに bind しています。
Generative AI の Access control については、以下にまとまっています。
https://cloud.google.com/vertex-ai/docs/generative-ai/access-control

なお、Cloud Run 上で動くアプリケーションは、Cloud Run で使用されるサービスアカウントの権限を自動で利用して各種 Google Cloud のサービスにアクセスするため、キーなどをアプリケーションに直接渡す必要はありません。
Application Default Credential を使って Vertex AI にアクセスする場合は、以下のコマンドを実行した上で、 Credential を空欄にしてください。

Authorization
Vertex AI の認証

なお、API ごとに異なるサービスアカウントを利用したい場合は、必要なサービスアカウントを準備し、Flowise で credentials を定義して使うこともできます。
(その場合は、json をダウンロードして "Connect Credential" 内に入力)

2. Secret Manager の利用

次は、環境変数を見ていきましょう。
先の例では、データベースの credentials をデプロイ時に環境変数として指定していましたが、この方法の場合、コンソールで以下のように見えてしまいます。

Cloud Run without Secret Manager
Secret Manager を使わない場合

コンソールから見えてしまっても問題ないケースも有ると思いますが、念の為 Secret Manager を使いましょう。

# Delete Cloud Run service
gcloud run services delete flowise --project=${PROJECT_ID}

# Create secrets
echo -n ${FLOWISE_PASSWORD} | gcloud secrets create flowise-password --project=${PROJECT_ID} --data-file=-
echo -n ${DATABASE_PASSWORD} | gcloud secrets create database-password --project=${PROJECT_ID} --data-file=-

# Add access to Secret Manager into Flowise service account
gcloud secrets add-iam-policy-binding flowise-password \
    --member="serviceAccount:sa-flowise@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/secretmanager.secretAccessor" --project=${PROJECT_ID}
gcloud secrets add-iam-policy-binding database-password \
    --member="serviceAccount:sa-flowise@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/secretmanager.secretAccessor" --project=${PROJECT_ID}

# Deploy Cloud Run Service using Secret Manager
gcloud beta run deploy flowise --port 3000 --region ${REGION} \
    --image ${REGION}-docker.pkg.dev/${PROJECT_ID}/flowiseai/flowiseai \
    --set-env-vars "FLOWISE_USERNAME=${FLOWISE_USERNAME},DATABASE_TYPE=postgres,DATABASE_PORT=5432,DATABASE_HOST=${DATABASE_HOST},DATABASE_NAME=flowise,DATABASE_USER=${DATABASE_USER}" \
    --network=default --subnet=default --vpc-egress=private-ranges-only --project=${PROJECT_ID} \
    --allow-unauthenticated \
    --service-account sa-flowise@${PROJECT_ID}.iam.gserviceaccount.com \
    --update-secrets=FLOWISE_PASSWORD=flowise-password:latest,DATABASE_PASSWORD=database-password:latest    # added

Secret Manager を使うと、以下のようにコンソールから値を確認することができなくなります(権限を持つ人だけが secret の中身を確認できるようになります)。

Cloud Run with Secret Manager
Secret Manager を使う場合

3. Cloud Run への ingress を制限

先程の構成では、Cloud Run に対して認証無しでアクセスできる設定になっていましたが、Cloud Run では上り(内向き = Cloud Run へのトラフィック)を制限する事ができます。

https://cloud.google.com/run/docs/securing/ingress?hl=ja

今回は、前述の通り、外部アプリケーションロードバランサを前段に置く構成を取るため、「内部と Cloud Load Balancing」という設定を選択します。

# Delete Cloud Run service
gcloud run services delete flowise --project=${PROJECT_ID}

# Deploy Cloud Run Service allowing internal access
gcloud beta run deploy flowise --port 3000 --region ${REGION} \
    --image ${REGION}-docker.pkg.dev/${PROJECT_ID}/flowiseai/flowiseai \
    --set-env-vars "FLOWISE_USERNAME=${FLOWISE_USERNAME},DATABASE_TYPE=postgres,DATABASE_PORT=5432,DATABASE_HOST=${DATABASE_HOST},DATABASE_NAME=flowise,DATABASE_USER=${DATABASE_USER}" \
    --network=default --subnet=default --vpc-egress=private-ranges-only --project=${PROJECT_ID} \
    --allow-unauthenticated \
    --service-account sa-flowise@${PROJECT_ID}.iam.gserviceaccount.com \
    --update-secrets=FLOWISE_PASSWORD=flowise-password:latest,DATABASE_PASSWORD=database-password:latest \
    --ingress=internal-and-cloud-load-balancing # added

この設定を入れると、 --allow-unauthenticated のオプションを指定していたとしても、アクセスができなくなっているはずです。

次に、外部アプリケーションロードバランサ経由で HTTPS でアクセスできるように設定していきましょう。
まず、static ip を作成します。

# Reserve static ip
gcloud compute addresses create flowise-ip \
    --network-tier=PREMIUM \
    --ip-version=IPV4 \
    --global --project=${PROJECT_ID}

# Confirm IP address
gcloud compute addresses describe flowise-ip \
    --format="get(address)" \
    --global --project=${PROJECT_ID}

次に、証明書リソースを作成します。
HTTPS ロードバランサを作成するには、ロードバランサのフロントエンドに SSL 証明書リソースを追加する必要があります。今回は、Google マネージド SSL 証明書を利用します。
https://cloud.google.com/load-balancing/docs/ssl-certificates/google-managed-certs?hl=ja

# Create ssl certificate
gcloud compute ssl-certificates create flowise-ssl-certificate \
    --description="SSL certificate for flowise" \
    --domains="flowise.example.com" \
    --global  --project=${PROJECT_ID}

次は、ロードバランサを作成しましょう。詳細な説明は以下を参考にしてください。
https://cloud.google.com/load-balancing/docs/https/setup-global-ext-https-serverless?hl=ja

gcloud compute network-endpoint-groups create flowise-serverless-neg \
    --region=${REGION} \
    --network-endpoint-type=serverless  \
    --cloud-run-service=flowise --project=${PROJECT_ID}

gcloud compute backend-services create flowise-backend-service \
    --load-balancing-scheme=EXTERNAL_MANAGED \
    --global --project=${PROJECT_ID}

gcloud compute backend-services add-backend flowise-backend-service \
    --global \
    --network-endpoint-group=flowise-serverless-neg \
    --network-endpoint-group-region=${REGION} --project=${PROJECT_ID}

gcloud compute url-maps create flowise-url-map \
    --default-service flowise-backend-service --project=${PROJECT_ID}

gcloud compute target-https-proxies create flowise-target-https-proxy \
    --http-keep-alive-timeout-sec=610 \
    --ssl-certificates=flowise-ssl-certificate \
    --url-map=flowise-url-map --project=${PROJECT_ID}

gcloud compute forwarding-rules create flowise-https-forwarding-rule \
    --load-balancing-scheme=EXTERNAL_MANAGED \
    --network-tier=PREMIUM \
    --address=flowise-ip \
    --target-https-proxy=flowise-target-https-proxy \
    --global \
    --ports=443 --project=${PROJECT_ID}

最後に、ドメインをロードバランサに接続します。
指定したドメインに、先程の IP アドレス を A レコードで追加してください。
https://cloud.google.com/load-balancing/docs/https/setup-global-ext-https-serverless?hl=ja#update_dns

暫く(数時間から最大72時間程度)待つと、更新された DNS A レコードが伝搬され、証明書のステータスが ACTIVE になります。
(私の場合は、十数分くらいでした)

gcloud compute ssl-certificates describe flowise-ssl-certificate \
    --format="get(managed.domainStatus)" --project=${PROJECT_ID}

証明書のステータスが ACTIVE になったら、 https://<ドメイン名> でアクセスすると Flowise にアクセスできます。

4. Cloud Armor による IP 制限 と IAP による Google アカウント認証

Cloud Armor は、DDoS 攻撃や XSS などのアプリケーション攻撃から、保護したり、 IP 制限をかけるためのサービスです。
今回は、先ほど作成したロードバランサに、IP 制限を追加してみましょう。
https://cloud.google.com/armor/docs/configure-security-policies?hl=ja#create-example-policies

なお、ルールが適用されるのに数分時間がかかることがあるので注意しましょう。

# Create security policy
gcloud compute security-policies create internal-users-policy \
    --description "policy for internal test users" --project=${PROJECT_ID}

# Configure default "Deny"
gcloud compute security-policies rules update 2147483647 \
    --security-policy internal-users-policy \
    --action "deny-502" --project=${PROJECT_ID}

# Configure "Alloy" policy
gcloud compute security-policies rules create 1000 \
    --security-policy internal-users-policy \
    --description "allow traffic from 198.51.100.0/24" \
    --src-ip-ranges "198.51.100.0/24" \
    --action "allow" --project=${PROJECT_ID}

# Update backend service
gcloud compute backend-services update flowise-backend-service \
    --security-policy internal-users-policy --global --project=${PROJECT_ID}

Cloud Armor を使うことで、アクセス元の IP を制限することができました。

更にセキュリティを高めるために、Google Cloud では IAP (Identity-Aware Proxy) という、ID とコンテキストを利用してアプリケーションへのアクセスを保護する機能が提供されています。Cloud Run は IAP に対応している為、 特定の許可した Google アカウントのみ にアクセスさせることも可能です。
詳細な手順については、以下のドキュメントを参照してください。
https://cloud.google.com/iap/docs/enabling-cloud-run?hl=ja

なお、IAP に加えて BeyondCorp Enterprise を導入することで、ユーザ ID に加えて、デバイスの稼働状況やその他のコンテキスト要素に基づいてきめ細かなアクセス制御を実現できます。例えば、「社用端末かつ最新のアップデートが適用された端末のみにアクセスを許可する」といったことが可能です。

さらなるセキュリティの高みへ

今回は、Cloud Run でアプリケーションを動かす際には是非使ってほしい機能を紹介しましたが、これ以外にも様々な機能 / プロダクトがあります。

  • 脆弱性スキャン (Artiract Registry)
    • コンテナイメージ内に脆弱性が含まれているかをチェック
  • Binary Authorization
    • イメージの情報を元に、信頼できるコンテナイメージのみをデプロイするための機能
  • Security Command Center Premium
    • Google Cloud のセキュリティの要となる機能
    • Cloud Run に直接関連するというより、Flowise で利用する際に必要なデータが置かれている BigQuery, Cloud Storage の設定ミスなどの検知に有用
    • プロジェクトレベル / 組織レベルの Pay as you go, Subscription などの料金形態が有り、小さく始めることも可能

Google Cloud には様々なセキュリティ製品が提供されているので、必要に応じてぜひ使ってみましょう。

Clean up

今回の検証で作成した主要なクラウドリソースの削除手順を記載します。
すべてのリソースが含まれているわけではないので、新規プロジェクトで検証した場合は、プロジェクトごと削除が推奨です。

gcloud artifacts repositories delete flowiseai --location=${REGION} --project=${PROJECT_ID}
gcloud run services delete flowise --project=${PROJECT_ID}
gcloud sql instances delete flowise-instance --project=${PROJECT_ID}
gcloud iam service-accounts delete sa-flowise@${PROJECT_ID}.iam.gserviceaccount.com --project=${PROJECT_ID}
Google Cloud Japan

Discussion