Container Service for Kubernetes (ACK) でコンテナ化されたワークロードを実行する場合、アプリケーションコードにアクセスキーをハードコーディングすると、セキュリティリスクが生じ、認証情報のローテーションが困難になります。KMS クラウドネイティブアクセスは、Pod に KMS Agent サイドカーをインジェクトしてこのリスクを排除し、アプリケーションがローカル HTTP リクエストを通じて KMS で管理される認証情報を安全に取得できるようになります。
仕組み
Key Management Service (KMS) のクラウドネイティブアクセス機能は、コンポーネントのインストールと設定のためのガイド付きワークフローを提供します。Container Service for Kubernetes (ACK) クラスター内の Pod が HTTP リクエストを送信すると、Helm コンポーネント ack-kms-agent-webhook-injector によって Pod に自動的にインジェクトされた KMS エージェントがリクエストを受信し、OpenID Connect (OIDC) JWT または Resource Access Management (RAM) ロールを使用して認証します。KMS が権限を検証すると、エージェントを通じて認証情報を返します。エージェントは認証情報をローカルにキャッシュすることで、繰り返しのリクエストを削減し、認証情報取得のレイテンシーを低減します。
適用範囲
ACK クラスタータイプの制限: ACK マネージドクラスター、ACK Serverless クラスター、および ACS クラスターに対応しています。
説明ACK 専用クラスターおよび ACK 登録クラスターについては、「ACK で KMS エージェントをデプロイしてシークレットを取得する」をご参照ください。
リージョンの制限: ACK クラスターと Key Management Service (KMS) インスタンスは、同じリージョンにある必要があります。
パフォーマンスの制限: 各 Pod は独立して KMS エージェントサイドカーコンテナを実行します。大量の Pod をデプロイし、認証中に Security Token Service (STS) トークンリクエストが 1 分あたり 500 を超えた場合、レート制限が作動し、KMS エージェントの正常な動作に影響を及ぼします。
ACK クラスターでの RRSA の有効化
クラスター作成時の有効化
ACK マネージドクラスターおよびACK エッジクラスターを作成する際、クラスター設定の 詳細オプション (選択してください) セクションで RRSA を有効化できます。
クラスター情報ページでの有効化
ACKコンソールにログインします。 左側のナビゲーションウィンドウで、[クラスター] をクリックします。
[クラスター] ページで、管理するクラスターの名前をクリックします。 左側のウィンドウで、[クラスター情報] をクリックします。
基本情報 タブの セキュリティと監査 セクションで、 [RRSA OIDC] の横にある 有効にする をクリックします。
RRSA の有効化 ダイアログボックスで、 OK をクリックします。
説明基本情報 ページで、クラスターのステータスが 更新中 から 実行中 に変わると、クラスターで RRSA 機能が有効になります。
KMS エージェントのインストール
ステップ 1:名前空間とサービスアカウントの作成 (オプション)
名前空間は、Container Service for Kubernetes (ACK) クラスターを開発、テスト、本番などのさまざまな環境に合わせて論理的に分離された仮想空間に分割します。異なる名前空間のアプリケーションは、デフォルトでは互いのリソースにアクセスできません。ビジネスアプリケーション用の名前空間とサービスアカウントが既にある場合は、このステップをスキップしてください。
名前空間を作成します。
YAML ファイルを使用して名前空間を作成します。次の例では、
app1-namespace.yamlを使用してapp1-devという名前の名前空間を作成します。apiVersion: v1 kind: Namespace metadata: name: app1-dev次のコマンドを実行して名前空間を作成します。
kubectl apply -f app1-namespace.yaml名前空間が作成されたことを確認します。出力に
app1-devが含まれていれば、名前空間は作成されています。kubectl get namespaces
サービスアカウントを作成します。
YAML ファイルを使用してサービスアカウントを作成します。次の例では、
app1-serviceaccount.yamlを使用して、前のステップで作成したapp1-dev名前空間にapp1-serviceという名前のサービスアカウントを作成します。apiVersion: v1 kind: ServiceAccount metadata: name: app1-service namespace: app1-dev次のコマンドを実行してサービスアカウントを作成します。
kubectl apply -f app1-serviceaccount.yamlサービスアカウントが作成されたことを確認します。出力に
app1-serviceが含まれていれば、サービスアカウントは作成されています。kubectl get serviceaccount -n app1-dev
ステップ 2:権限の設定
KMS クラウドネイティブアクセスは、次の 2 つの認証方式をサポートしています。
ほとんどのシナリオでは、設定がより簡単な OpenID Connect (OIDC) を推奨します。既存の RAM ロールポリシーを再利用する必要がある場合や、クロスアカウントアクセスが必要な場合は、RAM ロールを使用します。
認証方式 | 特徴 |
OIDC (ACK) | ACK クラスターは、OpenID Connect (OIDC) 標準の JWT を使用して認証します。エージェントはサービスアカウントトークンを自動的に取得し、Pod の ID を KMS に証明します。RAM ロールは不要で、設定が最も簡単です。 |
RAM ロール | RAM ロールの STS の一時的な認証情報を使用して KMS にアクセスします。この方法は、既存の RAM ロールの設定を再利用したいシナリオに適しています。 |
OIDC (ACK)
表示された設定パネルで、Authentication Method を [OIDC (ACK)] に設定します。
次のパラメーターを設定します:
パラメーター
説明
Namespace
Pod が存在する名前空間の名前 (例:
app1-dev)。ServiceAccount
Pod が使用するサービスアカウント (例:
app1-service)。PodNamePrefix
Pod 名のプレフィックス。このパラメーターを設定すると、名前がプレフィックスに一致する Pod のみが検証を通過できます。このパラメーターを設定しない場合、名前空間とサービスアカウントのみを検証します。
[スコープ]
KMS へのアクセス方法。有効な値は次のとおりです:
[Specified KMS Instance]:インスタンスエンドポイントを使用して、特定の KMS インスタンス内のキーと認証情報にアクセスします。
Shared KMS Gateway:KMS サービスエンドポイントを使用して、認証情報にアクセスします。
[Application Access Point Name]
識別や管理に用いる、アプリケーションアクセスポイント (アクセス認証情報) のカスタム名。
[Policy Name]
RAM ポリシーのカスタム名。このステップでポリシーを直接作成するため、RAM コンソールで事前に作成する必要はありません。
[RBAC の権限]
RBAC (ロールベースのアクセス制御) 権限レベル。アプリケーションの認証情報操作権限を決定します。
スコープに [Specified KMS Instance] が選択されている場合:
[CryptoServiceKeyUser]:KMS インスタンス内のキーを認証情報の暗号化に使用できます。
[CryptoServiceSecretUser]:KMS インスタンス内の認証情報を使用でき、インスタンスの認証情報 API をサポートします。
スコープに Shared KMS Gateway が選択されている場合:現在のアカウント配下のすべての認証情報を使用できる [SecretUser] のみがサポートされます。
[アクセス可能のリソース]
アプリケーションがアクセスする必要のある認証情報とキー (認証情報の暗号化および復号化に使用) を選択します。
重要複数の認証情報を選択し、認証情報名の合計長が制限を超えると、"parameter invalid" エラーが返されます。この場合、ワイルドカードを使用して許可される認証情報を指定します。例えば、
secret/rds-ibm*は、プレフィックスがrds-ibmの認証情報へのアクセスを許可します。[Description]
オプション。アプリケーションアクセスポイントの詳細な説明。最大 8,192 文字。
OK をクリックして続行します。
RAM ロール
ACK クラスターが STS にアクセスできることを確認します
STS サービスエンドポイント sts-intl.aliyuncs.com を ACK クラスターの出口ホワイトリストに追加し、ファイアウォールやその他のネットワーク防御ルールによってブロックされないようにします。
説明KMS エージェントで RAM ロール を使用するには、STS の
AssumeRoleWithOIDCAPI を呼び出して一時的な認証情報を取得する必要があります。エンドポイントがブロックされている場合、エージェントは一時的な認証情報を更新できません。ID プロバイダー情報を取得します。
対象のクラスター名をクリックして、詳細ページに移動します。
基本情報 タブの セキュリティと監査 セクションで、RRSA (RAM Roles for Service Accounts) OIDC の横にある [Enabled] ラベルにポインターを合わせると、プロバイダー URL と ARN 情報が表示されます。プロバイダー URL の形式は
https://oidc-ack-<region>.oss-<region>.aliyuncs.com/<cluster_id>で、プロバイダー ARN の形式はacs:ram::<account_id>:oidc-provider/ack-rrsa-<cluster_id>です。
RAM ロールを作成します。
信頼できるエンティティタイプとして IdP を選択し、[Switch to Editor] をクリックします。
[Visual Editor] セクションで、次の項目を設定します:
基本設定
パラメーター
説明
Effect
[Allow] を選択します。
Action
デフォルト値の
sts:AssumeRoleを維持します。Condition
キーが
oidc:sub、演算子がStringEquals、値がsystem:serviceaccount:<namespace>:<ServiceAccountName>となる条件を追加します。ここで、namespaceとServiceAccountNameは、ワークロードを実行している Pod の名前空間とサービスアカウント に対応します。次のようにプリンシパルを設定します:
[Principal] として [Identity Provider] を選択し、その下の [Edit] をクリックします。
[Identity Provider] 設定ページで、次のパラメーターを設定し、OK をクリックします。
パラメーター
説明
IdP Type
OIDC を選択します。
Identity Provider
RRSA が有効になった後、ACK クラスターによって自動的に作成された ID プロバイダーを選択します:
ack-rrsa-<cluster_id>。説明RAM ロールの信頼ポリシーと Pod テンプレート内の
audienceの値sts.aliyuncs.comを、KMS エージェントが使用する STS サービスエンドポイントと混同しないでください。audienceの値は、RRSA が有効になった後に作成される OIDC ID プロバイダーのクライアント ID です。これは、KMS エージェントが STS を呼び出すために使用するエンドポイントを決定するものではありません。
設定が完了したら、[OK] をクリックしてロール名 (例:
app1-rrsa) を設定し、確認 をクリックします。
ポリシーを作成し、RAM ロールにアタッチします。詳細については、「カスタムポリシーの作成」および「RAM ロールへのポリシーのアタッチ」をご参照ください。
ポリシーの作成 をクリックし、[Script Editor] を選択して、次の例を使用してポリシーを設定します。
説明この例では、ポリシー名は
dev-role-for-rrsa-kms-policyで、タグsecret:app1が付与された認証情報へのアクセスのみを許可します。{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "kms:Decrypt", "kms:GetSecretValue" ], "Resource": "*", "Condition": { "StringEqualsIgnoreCase": { "kms:tag/secret": [ "app1" ] } } } ] }ポリシーリストに戻り、対象のポリシーを見つけて、[Actions] 列の [Attach to Identity] をクリックします。
[Principal] セクションで、作成した RAM ロールを選択し、[Confirm] をクリックします。
ステップ 3:ack-kms-agent-webhook-injector のインストール
[Helm] ページで、デプロイ をクリックします。基本情報 セクションを設定し、次 をクリックします。
パラメーター
説明
[アプリケーション名]
デフォルトのアプリケーション名
ack-kms-agent-webhook-injectorを使用します。[名前空間]
デフォルトのチャート名前空間
kube-systemを使用します。ACK クラスターごとに 1 つの名前空間にインストールし、複数回インストールする必要はありません。[発生元]
デフォルト: Marketplace。このパラメーターは変更できません。
Chart
ack-kms-agent-webhook-injectorを検索して選択します。確認ダイアログが表示されたら、情報を確認して 可 をクリックします。
パラメーター ページで、ステップ 2 で選択した認証方式に基づいてパラメーターを設定します。
OIDC (ACK):デフォルトの設定を維持します。
RAM ロール:
agent.auth.roleArnを空のままにし、agent.auth.roleArnMappingを<Namespace>:<ServiceAccountName>:<RAM Role ARN>に設定します。次の例では、ステップ 2 で作成したデータを使用します:説明NamespaceとServiceAccountNameは、ワークロードを実行している Pod の名前空間とサービスアカウント に対応します。RAM Role ARNは、RAM ロールの詳細ページで確認できます。agent: auth: roleArn: roleArnMapping: app1-dev:app1-service: acs:ram::190325303126****:role/app1-rrsa
設定が完了したら、OK をクリックします。アプリケーションの詳細ページにリダイレクトされます。
ステップ 4:エージェントサイドカーのインジェクト
Pod アノテーションの追加:アノテーションキーを
kms-agent-webhook-injector/injectに、値をtrueに設定します。ワークロードが実行されている名前空間に切り替え、デプロイメントに Pod アノテーションを追加します。
重要RAM ロール認証方式の場合、デプロイメントの YAML 設定を変更し、
ServiceAccountNameパラメーターをワークロードを実行している Pod のサービスアカウント名 (例:app1-service) に設定します。OIDC (ACK) 認証方式の場合、変更は不要です。新しいデプロイメントの作成
[イメージによる作成]
デプロイメントリストの上にある イメージによる作成 をクリックし、パラメーターを設定します。
上級 を設定する際、Labels and Annotations セクションに移動し、Pod アノテーションを追加します: 名前 列に
kms-agent-webhook-injector/injectを、値 列にtrueを入力します。作成する をクリックして完了します。
[YAML のリソースの作成]
デプロイメントリストの上にある YAML のリソースの作成 をクリックします。
YAML ファイルを編集し、
spec.template.metadata.annotationsの下にkms-agent-webhook-injector/inject: "true"を追加します (このセクションが存在しない場合は作成します)。作成する をクリックして完了します。
既存のデプロイメントの変更
対象のワークロードを見つけ、アクション > 詳細 をクリックします。
詳細ページで、右上隅の YAML の編集 をクリックします。
spec.template.metadata.annotationsの下にkms-agent-webhook-injector/inject: "true"を追加します (このセクションが存在しない場合は作成します)。更新 をクリックし、ワークロードの準備が完了するのを待ちます。
インジェクションの確認
ポッド タブで、イメージ 列を確認します。KMS エージェントがサイドカーとして Pod にインジェクトされていることを確認できます。
説明Pod に KMS エージェントが 2 回インジェクトされることがあります。これは、初期化のために init コンテナが使用されるためです。init コンテナは初期化が完了すると終了 (Terminated) し、アプリケーションに悪影響を与えたり、コンピューティングリソースを継続的に消費したりすることはありません。
アプリケーション統合
デプロイメントに KMS Agent がインジェクトされると、アプリケーションコンテナは KMS Agent への HTTP リクエストを介して KMS から認証情報を取得できます。コードに AccessKey を設定する必要はありません。以下の例では、認証情報を取得する方法を示します。<SecretId> を実際の認証情報名に置き換えてください。
KMS エージェントは 127.0.0.1 でのみリッスンします。つまり、同じマシン上のアプリケーションまたはプロセスのみが通信できます。外部ネットワークデバイスは接続できません。アクセスアドレスは localhost または 127.0.0.1 のみをサポートしており、アプリケーションのローカル IP はサポートしていません。次の例では localhost を使用します。
KMS エージェントによるアクセス方式に加えて、KMS は SDK 経由でのアクセスもサポートしています。具体的な操作については、Secrets Manager クライアントをご参照ください。
OIDC (ACK)
curlの使用
次のコマンドを実行して、認証情報を取得します。
# ファイルからトークンを読み取り、AapArn を指定 curl -v -H "X-KMS-Token:$(</var/run/kmstoken/token)" -H "AapArn:<AapArn>" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'The
$(<file)構文は、bash や zsh などのシェルでのみサポートされています。 お使いの Pod ベースイメージが、この構文をサポートしていないシェル (デフォルトシェルが BusyBox ash である Alpine など) を使用している場合は、代わりに次のコマンドを使用してください。# ファイルからトークンを読み取り、AapArn を指定 (Alpine/BusyBox 互換) curl -v -H "X-KMS-Token:$(cat /var/run/kmstoken/token)" -H "AapArn:<AapArn>" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'
wgetの使用
次のコマンドを実行して、認証情報を取得します。
# ファイルからトークンを読み取り、AapArn を指定 wget -q -O - --header "X-KMS-Token:$(</var/run/kmstoken/token)" --header "AapArn:<AapArn>" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'$(<file)構文は、bashやzshなどのシェルでのみサポートされています。Pod のベースイメージで Alpine (デフォルトのシェルはash) を使用している場合や、デフォルトのシェルがこの構文をサポートしていない他のイメージを使用している場合は、代わりに次のコマンドを使用してください。# ファイルからトークンを読み取り、AapArn を指定 (Alpine 互換) wget -q -O - --header "X-KMS-Token:$(cat /var/run/kmstoken/token)" --header "AapArn:<AapArn>" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'
Goコード例
次の例では、Go を使用して認証情報を取得する方法を説明します。
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// versionStage または versionId を指定して、特定の認証情報バージョンを取得できます。
// 次の例では、versionId で認証情報を取得します。
// url := fmt.Sprintf("http://localhost:2025/secretsmanager/get?secretId=%s&versionId=%s", "agent-test", "version-id")
aapArn := "acs:kms:cn-hangzhou:19*********224:applicationaccesspoint/****"
url := fmt.Sprintf("http://localhost:2025/secretsmanager/get?secretId=%s", "agent-test")
token, err := ioutil.ReadFile("/var/run/kmstoken/token")
if err != nil {
fmt.Printf("error reading token file: %v\n", err)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Printf("error creating request: %v\n", err)
}
req.Header.Add("X-KMS-Token", string(token))
req.Header.Add("AapArn", aapArn)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("error sending request: %v \n", err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("status code %d - %s \n", resp.StatusCode, string(body))
}RAM ロール
curlの使用
次のコマンドを実行して、認証情報を取得します。
# ファイルからトークンを読み取り curl -v -H "X-KMS-Token:$(</var/run/kmstoken/token)" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>' # またはトークンを直接記述 curl -v -H "X-KMS-Token:<token>" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'The
$(<file)構文は、bash や zsh などのシェルでのみサポートされています。Pod のベースイメージで Alpine (デフォルトシェルは BusyBox ash) を使用している場合や、この構文をサポートしない他のイメージを使用している場合は、代わりに次のコマンドを使用してください:# ファイルからトークンを読み取り (Alpine/BusyBox 互換) curl -v -H "X-KMS-Token:$(cat /var/run/kmstoken/token)" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'
wgetの使用
次のコマンドを実行して、認証情報を取得します。
# ファイルからトークンを読み取り wget -q -O - --header "X-KMS-Token:$(</var/run/kmstoken/token)" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>' # またはトークンを直接記述 wget -q -O - --header "X-KMS-Token:<token>" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'$(<file)構文は、bash や zsh などのシェルでのみサポートされています。Pod のベースイメージが Alpine (デフォルトシェルは ash) である場合、またはデフォルトシェルがこの構文をサポートしていないその他のイメージである場合は、代わりに次のコマンドを使用してください。# ファイルからトークンを読み取り (Alpine 互換) wget -q -O - --header "X-KMS-Token:$(cat /var/run/kmstoken/token)" 'http://localhost:2025/secretsmanager/get?secretId=<SecretId>'
Goコード例
次の例では、Go を使用して認証情報を取得する方法を説明します。
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// versionStage または versionId を指定して、特定の認証情報バージョンを取得できます。
// 次の例では、versionId で認証情報を取得します。
// url := fmt.Sprintf("http://localhost:2025/secretsmanager/get?secretId=%s&versionId=%s", "agent-test", "version-id")
url := fmt.Sprintf("http://localhost:2025/secretsmanager/get?secretId=%s", "agent-test")
token, err := ioutil.ReadFile("/var/run/kmstoken/token")
if err != nil {
fmt.Printf("error reading token file: %v\n", err)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Printf("error creating request: %v\n", err)
}
req.Header.Add("X-KMS-Token", string(token))
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("error sending request: %v \n", err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("status code %d - %s \n", resp.StatusCode, string(body))
}課金
KMS 側のコスト:
サブスクリプション:KMS エージェントを使用する前に KMS インスタンスを購入する必要があります。KMS エージェント自体に追加料金はかかりません。詳細については、「Subscription」をご参照ください。
従量課金:すでに発生している料金に加えて、KMS エージェントが API コールを通じて認証情報を取得する際に、追加の QPS 料金が発生します。詳細については、「Pay-as-you-go」をご参照ください。
ACK 側のコスト:
ack-kms-agent-webhook-injector コンポーネントは無料です。インジェクトされたサイドカーおよび Webhook のワークロードが消費するコンピューティングリソースには、追加のコストが発生する場合があります。
ack-kms-agent-webhook-injector コンポーネントをインストールすると、Webhook サービスのワークロードが生成されます。このワークロードはコンピューティングリソースを消費し、コストが発生します。設定ファイルで、このワークロードの CPU とメモリの使用量を制限してください。
対象のワークロードを作成または更新すると、ack-kms-agent-webhook-injector は、KMS エージェントをサイドカーとしてコンテナにインジェクトします。KMS エージェントはコンピューティングリソースを消費し、コストが発生します。
トラブルシューティング
KMS エージェントのインストール時または使用時に問題が発生した場合は、次の一般的な問題を参照してトラブルシューティングしてください。
原因 | 解決策 |
エージェントのインストールに失敗 |
|
ネットワーク到達不能またはタイムアウト | ACK クラスターが KMS サービスに到達できることを確認してください。 VPC 経由で KMS にアクセスする場合、ACK クラスターが存在する VPC が KMS インスタンスとネットワーク接続されていることを確認してください。 |
KMS エージェントが、 | 次のようにトラブルシューティングしてください。
注:キャッシュされたシークレット値の読み取りが成功しても、RAM ロールの認証情報チェーンが正常であることを証明するものではありません。ログを確認して、 |
権限が不十分 | 次の設定を確認してください。
|
エージェントのインジェクトに失敗 | ack-kms-agent-webhook-injector コンポーネントが正しくインストールされ、正常に実行されていること、および Pod のアノテーション |
RRSA が有効になっていない | OIDC (ACK) 認証方式は、RRSA (RAM Roles for Service Accounts) 機能に依存します。 ACK コンソールで、クラスターのセキュリティと監査モジュールに移動して RRSA を有効にしてください。 詳細な手順については、ステップ 2 の [RAM ロール] タブにある ID プロバイダーの設定をご参照ください。 |