制限事項
セルフマネージドの Web アプリケーションに埋め込むことができるのは、Managed Service for Grafana の共有エディションのダッシュボードのみです。
背景情報
Grafana ダッシュボードをセルフマネージドの Web アプリケーションに埋め込むことで、以下のことが可能になります。
ステップ 1:RAM ユーザーの作成
お使いの Alibaba Cloud アカウントを使用して RAM ユーザーを作成し、そのユーザーにセキュリティトークンサービス (STS) の AssumeRole 操作を呼び出す権限を付与します。
-
お使いの Alibaba Cloud アカウント (ルートアカウント) または RAM 管理者として Resource Access Management (RAM) コンソールにログインします。
-
左側のナビゲーションウィンドウで、[ID] > [ユーザー] を選択します。
-
[ユーザー] ページで、[ユーザーの作成] をクリックします。
-
ユーザーの作成 ページで、[ログオン名]とDisplay nameを[ユーザーアカウント情報] セクションに入力します。アクセス方法 セクションで[コンソールアクセス]を選択し、確定する をクリックします。
重要
セキュリティ上の理由から、RAM コンソールでは AccessKey Secret が一度しか表示されません。必ず安全な場所に保存してください。
-
権限を新規付与する パネルの 権限ポリシー セクションに AliyunSTSAssumeRoleAccess ポリシーを追加し、[OK] をクリックします。
ステップ 3:一時的な認証情報の取得
セルフマネージドの Web アプリケーションにログインします。Web サーバーで、STS の AssumeRole 操作を呼び出して、一時的な認証情報 (一時的な AccessKey ID、AccessKey Secret、STS トークン) を取得します。AssumeRole 操作の詳細については、「AssumeRole」をご参照ください。
AssumeRole 操作は、以下のいずれかの方法で呼び出すことができます。
このトピックでは、Java SDK を例として使用します。
Java SDK を使用する際は、次のパラメーターを指定します。
String accessKey = "<accessKeyId>"; // RAM ユーザーの AccessKey ID。
String accessSecret = "<accessKeySecret>"; // RAM ユーザーの AccessKey Secret。
String roleArn = "<roleArn>"; // RAM ロールの ARN。
AccessKey ID と AccessKey Secret は、RAM ユーザーの作成時に取得します。詳細については、「AccessKey の作成」をご参照ください。
RAM ロールの Alibaba Cloud リソース名 (ARN) を取得するには、次の手順を実行します。
-
RAM コンソールのページで、一覧でターゲット RAM ロールの名前をクリックします。
-
ロール詳細ページで、基本情報 セクションにある Amazon リソースネーム (ARN) をコピーします。
ステップ 4:ログイントークンの取得
STS の AssumeRole 操作を呼び出して一時的な認証情報を取得した後、ログインサービスを呼び出してログイントークンを取得します。
重要
STS から返される STS トークンには、特殊文字が含まれている場合があります。使用する前に、これらの文字を URL エンコードする必要があります。
リクエストの例:
https://signin.alibabacloud.com/federation?Action=GetSigninToken
&AccessKeyId=<The temporary AccessKey ID returned by STS>
&AccessKeySecret=<The temporary AccessKey secret returned by STS>
&SecurityToken=<The STS token returned by STS>
説明
仮想ネットワークオペレーターの場合は、&TicketType=mini パラメーターも追加する必要があります。
ステップ 5:ログイン不要の URL の生成
ログイントークンと埋め込みたい Grafana ダッシュボードの URL を使用して、ログイン不要の URL を生成します。この URL を使用すると、ユーザーはセルフマネージドの Web アプリケーションからログインせずに Grafana ダッシュボードにアクセスできます。
説明
ログイントークンの有効期間は 3 時間です。リクエストごとに新しいログイントークンを生成するように Web アプリケーションを設定することを推奨します。
-
Grafana で、埋め込みたいダッシュボードの URL を取得します。
説明
-
左側のナビゲーションウィンドウを非表示にするには、URL のクエリ文字列に kiosk=tv を追加します。
-
上部のナビゲーションバーと左側のナビゲーションウィンドウを非表示にするには、URL のクエリ文字列に kiosk を追加します。
-
上部のナビゲーションバーと左側のナビゲーションウィンドウを非表示にし、右上隅の時間範囲ピッカーを保持するには、URL のクエリ文字列に kiosk=tv2 を追加します。
-
ログイントークンと Grafana ダッシュボードの URL を使用して、ログイン不要の URL を生成します。
http://signin.alibabacloud.com/federation?Action=Login
&LoginUrl=<The URL to which users are redirected if the logon fails. This is typically the URL that your application configures for 302 redirects.>
&Destination=<The URL of the Grafana dashboard>
&SigninToken=<The logon token you obtained>
-
ブラウザでログイン不要の URL を開きます。
サンプルコード
/*
Copyright 2022 Alibaba Cloud.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.aliyun.arms.unifydemo.unifydemo;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.net.URLEncoder;
/**
* @author XX
* @version V1.0.0
* @dept XX-XX
* @date 2025/7/2 16:10
* @description
*/
public class GrafanaIframeUrlGenerator {
private static final String signInHost = "https://signin.aliyun.com";
private static final String loginUrl = "https://www.aliyun.com";
private static final String loginUri = "/federation?Action=Login&LoginUrl=%s&Destination=%s&SigninToken=%s";
private static final String signInTokenUri = "/federation?Action=GetSigninToken&AccessKeyId=%s&AccessKeySecret=%s&SecurityToken=%s&TicketType=%s";
public static void main(String[] args) {
try {
// 送信先の Grafana ダッシュボードの URL。仮想ネットワークオペレーターの場合、URL には通常「4service」が含まれます。
String destination = "https://gnew4servims.console.alibabacloud.com/d/1098370038******-53945-422/ack-pro-apiserver?orgId=9&refresh=60s";
String regionId = "cn-hangzhou";// ご利用のリージョン ID を指定します。
// RAM ユーザーの AccessKey ID。このユーザーには AliyunSTSAssumeRoleAccess ポリシーがアタッチされている必要があります。
String accessKey = "";
String secretKey = "";// ご利用のアカウントとロールに基づいてこのパラメーターを設定します。
// 偽装する RAM ロールの ARN。アクセス失敗を防ぐために、このロールには ARMS に対する少なくとも読み取り専用権限 (例:AliyunARMSReadOnlyAccess) が必要です。
String role="acs:ram::109837003******:role/armsreadonlyforgrafanaiframe";
/*
* ステップ 1
* */
// パラメーターを設定し、ロールの ARN を指定し、STS トークンの権限をさらに制限するポリシーを設定します。// acs:ram::$accountID:role/$roleName
// AssumeRole リクエストを構築します。
AssumeRoleResponse.Credentials key = getCredentials(regionId, accessKey, secretKey, role, "role-" + System.currentTimeMillis());
/*
* ステップ 2:ログイントークンの取得
* */
String token = getLoginToken(key, destination);
/*
* ステップ 3:ログイン不要の URL の取得
* */
String url = getUrl(token, destination);
/*
* ステップ 4:URL へのリダイレクト
* */
System.out.println(url);
} catch (Error e) {
e.printStackTrace();
}
}
private static AssumeRoleResponse.Credentials getCredentials(String regionId, String accessKey, String secretKey, String roleArn, String roleSessionName) {
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKey, secretKey);
IAcsClient client = new DefaultAcsClient(profile);
AssumeRoleRequest request = new AssumeRoleRequest();
request.setSysMethod(MethodType.POST);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
request.setDurationSeconds(3600L);
try {
AssumeRoleResponse response = client.getAcsResponse(request);
return response.getCredentials();
} catch (Exception e) {
throw new RuntimeException("AssumeRole 操作の呼び出しに失敗しました。エラー: " + e.getMessage());
}
}
public static String getLoginToken(AssumeRoleResponse.Credentials key, String destination) {
String token = "";
if (key == null) {
return "";
}
String ticketType = "normal";
if (destination == null || destination.trim().length() == 0 || destination.contains("4service")) {
ticketType = "mini";
}
String signInTokenUrl = "";
try {
signInTokenUrl = signInHost + String.format(signInTokenUri,
URLEncoder.encode(key.getAccessKeyId(), "utf-8"),
URLEncoder.encode(key.getAccessKeySecret(), "utf-8"),
URLEncoder.encode(key.getSecurityToken(), "utf-8"),
URLEncoder.encode(ticketType, "utf-8"));
} catch (Exception e) {
throw new RuntimeException("signInTokenUrl の構築に失敗しました。エラー: " + e.getMessage());
}
final CloseableHttpClient httpClient = HttpClients.createDefault();
try {
HttpGet signInGet = new HttpGet(signInTokenUrl);
HttpResponse httpResponse = httpClient.execute(signInGet);
String signInToken = "";
if (httpResponse.getStatusLine().getStatusCode() != 200) {
throw new RuntimeException("ログイントークンの取得に失敗しました。");
}
String signInRes = EntityUtils.toString(httpResponse.getEntity());
signInToken = JSON.parseObject(signInRes).getString("SigninToken");
if (signInToken == null) {
throw new RuntimeException("ログイントークンが空です。応答: " + signInRes);
}
return signInToken;
} catch (Exception e) {
throw new RuntimeException("ログイントークンの取得に失敗しました。エラー: " + e.getMessage());
}
}
public static String getUrl(String token, String destination) {
String url = "";
try {
url = signInHost + String.format(loginUri,
URLEncoder.encode(loginUrl, "utf-8"),
URLEncoder.encode(destination, "utf-8"),
URLEncoder.encode(token, "utf-8"));
} catch (Exception e) {
throw new RuntimeException("ログイン URL の構築に失敗しました。エラー: " + e.getMessage());
}
return url;
}
}