通過API Key鑒權方式產生免登入的可分享的大盤連結。
背景資訊
原生的Grafana若需要直接存取大盤,要麼使用Snapshot功能,要麼開啟匿名模式。前者對訪問的資料做了鏡像,隨著時間變化無法查看後續更新的資料,後者若不配合IP白名單功能,則安全性較差。
可觀測可視化 Grafana 版支援通過API Key鑒權方式產生免登入的可分享的大盤連結。
您可以將其分享給其他使用者。
使用這個連結的使用者將能夠訪問指定的大盤而無需登入,因為鑒權是通過API Key完成的。
步驟一:配置Grafana參數
登入可觀測可視化 Grafana 版控制台,在左側導覽列單擊工作區管理。
在工作區管理頁面,單擊目標工作區ID。
在左側導覽列單擊參數設定。
在左側參數列表選擇aliyun,然後單擊修改參數。
修改api_key_share參數的運行參數為true,然後單擊儲存並生效。
可選:若期望連結用於iFrame內嵌,則需要額外調整以下參數。
跨域情況下
網域名稱需為HTTPS,並修改以下3個security參數。
allow_embedding=true cookie_samesite=none cookie_secure=true
未跨域情況下
修改security下的allow_embedding參數為true,開啟iFrame內嵌即可。
步驟二:建立API Key
Grafana 9.0.x和Grafana 10.0.x版本建立API Key的步驟有所不同,請確認您的Grafana版本並選擇對應的操作步驟。
如果您的Grafana是從9.0.x升級到10.0.x版本,那麼在Administration > api keys頁面依然可以看到Grafana 9.0.x中建立的API Key。此時,單擊migrate to service account可以將原有的API Key遷移到Service Account,遷移完成後api keys頁面將會被永久隱藏。此外,版本升級不會影響原有API Key的使用。
Grafana 9.0.x版本
登入可觀測可視化 Grafana 版控制台,在左側導覽列單擊工作區管理。
在工作區管理頁面,單擊目標工作區右側的訪問地址URL連結進入Grafana。
說明如果需要登入Grafana,可以使用Grafana的Admin帳號和建立工作區時設定的密碼登入Grafana,或單擊Sign in with Alibaba Cloud直接使用當前購買工作區的阿里雲帳號登入Grafana。
單擊Grafana首頁左上方的表徵圖。
在Grafana左側導覽列選擇
。說明進入該菜單需要Admin許可權。
單擊New API key或Add API key,然後設定以下參數。
參數
說明
Key name
API Key的名稱,不可以和已有的名稱重複。
Role
設定為Viewer。
Time to live
設定有效時間,例如60s(60秒)、10m(10分鐘)、1d(1天)。
單擊Add,在彈出的對話方塊中擷取並儲存API Key的值。
重要對話方塊關閉後將無法再次查看API Key的值。
Grafana 10.0.x版本
登入可觀測可視化 Grafana 版控制台,在左側導覽列單擊工作區管理。
在工作區管理頁面,單擊目標工作區右側的訪問地址URL連結進入Grafana。
說明如果需要登入Grafana,可以使用Grafana的Admin帳號和建立工作區時設定的密碼登入Grafana,或單擊Sign in with Alibaba Cloud直接使用當前購買工作區的阿里雲帳號登入Grafana。
單擊Grafana首頁左上方的表徵圖。
在Grafana左側導覽列選擇
。重要進入該菜單需要Admin許可權。
服務帳號會佔用一個使用者帳號。
單擊Add service account,然後設定以下參數,然後單擊Create。
參數
說明
Display name
Service Account的名稱,不可以和已有的名稱重複。
Role
設定為Viewer。
單擊頁面右側的Add service account token,然後設定以下參數。
參數
說明
Display name
API Key的名稱,不可以和已有的名稱重複。
Expiration
設定有效時間,
No Expiration:無終止日期
Set Expiration date:設定有效期間
Expiration date
如果在Expiration中選擇Set Expiration date,則您需要設定有效期間的截止日。
單擊Generate token,然後在彈出的對話方塊單擊Copy to clipboard and close。
對話方塊關閉後將無法再次查看API Key的值。
步驟三:產生分享連結
Grafana 9.0.x版本
在Grafana頁面,進入需要分享的大盤頁面。
單擊表徵圖,在Link頁簽擷取大盤分享連結。
在連結最後添加
&aliyun_api_key=<API Key值>
,API Key值為上文步驟二中擷取的API Key。https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key=eyJr****WkIwNnN2c0RTSD******
使用帶有API Key的分享連結即可免登入訪問Grafana大盤。
Grafana 10.0.x版本
在Grafana頁面,進入需要分享的大盤頁面。
單擊表徵圖,在連結頁簽擷取大盤分享連結。
在連結最後添加
&aliyun_api_key=<API Key值>
,API Key值為上文步驟二中擷取的API Key。https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key=eyJrIjoiWkIwNnN2c0RTSD******
使用帶有API Key的分享連結即可免登入訪問Grafana大盤。
步驟四:產生高安全性分享連結(可選)
步驟三:產生分享連結中產生的分享連結需要定期更換API Key,以避免API Key泄露造成的資料安全風險。本步驟分別介紹在9.0.x版本API Key和10.0.x版本Service Account Token情境下產生高安全性分享連結的方法。10.0.x版本後API Key稱之為Service Account Token。
在進行本步驟前,您需要先修改api_key_share_version的運行參數為v2。
登入可觀測可視化 Grafana 版控制台,在左側導覽列單擊工作區管理。
在工作區管理頁面,單擊目標工作區ID,然後在左側導覽列單擊參數設定。
修改api_key_share_version參數的運行參數為v2,然後單擊儲存並生效。
9.0.x版本API Key
將步驟二:建立API Key中擷取的API Key進行Base64解密。
Base64是網路上常見的用於傳輸8Bit位元組碼的編碼方式之一,Base64是一種基於64個可列印字元來表示二進位資料的方法。
搜尋常用工具網站進行解密,例如base64。
Java語言解密。
package main import java.util.Base64; public class Base64Example{ public static void main(String[] args) { String apiKey = "eyJr****REpzZGYzd2JIa0N3ekgyWjlWWmhrSTM5bWdGT2hGSmwiLCJuIjoidGVzdDEiLCJpZCI6MX0="; String decodeKey = new String(Base64.getDecoder().decode(apiKey)); System.out.println(decodeKey); } }
運行結果:
{"k":"DJsd****HkCwzH2Z9VZhkI39mgFOhFJl","n":"test1","id":1}
Go語言解密。
package main import "fmt" import "encoding/base64" func main() { apiKey := "eyJr****REpzZGYzd2JIa0N3ekgyWjlWWmhrSTM5bWdGT2hGSmwiLCJuIjoidGVzdDEiLCJpZCI6MX0=" decodeKey, err := base64.StdEncoding.DecodeString(apiKey) if err != nil { fmt.Println(err.Error()) return } fmt.Println(string(decodeKey)) }
運行結果:
{"k":"DJsd****HkCwzH2Z9VZhkI39mgFOhFJl","n":"test1","id":1}
將上述解密的API Key值中的k進行PBKDF2加密。
PBKDF2(Password-Based Key Derivation Function 2)是一種基於密碼的密鑰匯出函數,用於從使用者提供的密碼和一些其他參數(如鹽值和迭代次數)安全地派生出加密金鑰。它主要應用於需要儲存使用者密碼的情境,旨在確保即使資料庫被泄露,攻擊者也難以直接擷取到使用者的純文字密碼或輕易破解派生出的密鑰。
加密有多種方式,以下是加密參數說明。
參數
說明
鹽salt
設定為API Key的名稱Name(即上面解密出來的n值),本樣本中為test1。
迭代次數iteration
設定為:10000
輸出位元組長度output len
設定為:50
加密金鑰長度key size
設定為:256
輸出類型
設定為:Hex
搜尋常用網站工具加密,例如df2。
Java語言加密。
package main import javax.crypto.SecretKeyFactory; import java.security.GeneralSecurityException; import javax.crypto.spec.PBEKeySpec; import java.security.spec.KeySpec; public class PBKDFExapmle { public static void main(String[] args) { String password = "DJsd****HkCwzH2Z9VZhkI39mgFOhFJl"; String salt = "test1"; int iterationCount = 10000; int outputLength = 50 * 8; try { KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), iterationCount, outputLength); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); byte[] keyBytes = skf.generateSecret(spec).getEncoded(); System.out.println(bytesToHex(keyBytes)); } catch (GeneralSecurityException e) { e.printStackTrace(); } } private static String bytesToHex(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } }
輸出:PBKDF2 Password
1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****
Go語言加密。
package main import "fmt" import "encoding/hex" import "crypto/sha256" import "golang.org/x/crypto/pbkdf2" func main() { password:="DJsd****HkCwzH2Z9VZhkI39mgFOhFJl" salt="test1" newPasswd := pbkdf2.Key([]byte(password), []byte(salt), 10000, 50, sha256.New) encodePassword:= hex.EncodeToString(newPasswd) fmt.Println(encodePassword) }
輸出:PBKDF2 Password
1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****
基於PBKDF2加密後的資訊構造MD5摘要簽名參數。
MD5資訊摘要演算法(MD5 Message-Digest Algorithm),是一種被廣泛使用的密碼散列函數,可以產出一個128位(16位元組)的散列值(hash value),用於確保資訊傳輸完整一致。
加密內容為:<PBKDF2 Password>+"_"+<當前系統時間整數,單位:秒>
例如:
PBKDF2 Password為:1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****
當前系統時間為:2024-09-20 17:12:13 ,則精確到秒的時間整數為:1726823533
所以需要MD5簽名的整體資訊為:1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****_1726823533
MD5簽名有多種方式:
搜尋常用網站工具加密,例如MD5。
Java代碼進行MD5簽名。
package main; import java.security.MessageDigest; public class MD5 { public static void main(String[] args) { String pbkdfPassword = "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****"; long timeSeconds=System.currentTimeMillis()/1000; String key=pbkdfPassword+"_"+timeSeconds; System.out.println(MD5.getMD5String(key,"UTF-8")); } public static String getMD5String(String str, String charset) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(str.getBytes(charset)); byte[] byteArray = messageDigest.digest(); StringBuffer md5StrBuff = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { md5StrBuff.append("0").append( Integer.toHexString(0xFF & byteArray[i])); } else { md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } } return md5StrBuff.toString().toLowerCase(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("MD5 error:"+e.getMessage()); } } }
Go代碼進行MD5簽名。
package main import ( "crypto/md5" "encoding/hex" "fmt" "io" "time" "strconv" ) func main() { // 需要計算MD5的字串 pbkdfPassword := "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****" timeSeconds:= time.Now().Unix() key:=pbkdfPassword+"_"+strconv.FormatInt(timeSeconds, 10) // 使用MD5包計算字串的MD5值 hash := md5.New() io.WriteString(hash, key) md5Str := hash.Sum(nil) // 將二進位的MD5值轉換成十六進位字串 md5StrHex := hex.EncodeToString(md5Str) fmt.Println("MD5 of", key, "is", md5StrHex) }
基於產生的MD5簽名資訊拼接連結參數。
參數說明:
參數名
說明
樣本
aliyun_api_key_sign
MD5簽名,隨時間變化。
例如:c3bf89b867cc88df72d507edc4d1****
aliyun_api_key_timestamp
簽章時間,當簽章時間和系統時間超過1分鐘後,簽名到期失效。
例如:1726823533
aliyun_api_key_name
API Key的名稱。
例如:test1
aliyun_api_key_org_id
API Key所在的組織Org ID。
例如:1
aliyun_api_key_expire_seconds
MD5簽名登入後失效時間,單位:秒。
預設:3600
樣本如下:
https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key_sign=c3bf89b867cc88df72d507edc4d1****&aliyun_api_key_timestamp=1726823533&aliyun_api_key_name=test1&aliyun_api_key_org_id=1
至此您可以基於程式碼每次動態產生更安全的免密登入Grafana的連結,同時可以避免API Key泄露問題。
10.0.x版本Service Account Token
將步驟二:建立API Key中的API Key(10.0.x版本後稱之為Service Account Token)切分後進行PBKDF2加密。
PBKDF2(Password-Based Key Derivation Function 2)是一種基於密碼的密鑰匯出函數,用於從使用者提供的密碼和一些其他參數(如鹽值和迭代次數)安全地派生出加密金鑰。它主要應用於需要儲存使用者密碼的情境,旨在確保即使資料庫被泄露,攻擊者也難以直接擷取到使用者的純文字密碼或輕易破解派生出的密鑰。
將Service Account Token按“_”進行切分。
#以下列Service Account Token為例: Token:glsa_yV9HAOVCjNKkvKoLMiypOc5T0Oov****_4f5ff3ce #切分後為: 首碼:glsa Secret:yV9HAOVCjNKkvKoLMiypOc5T0Oov**** Salt:4f5ff3ce
加密有多種方式,以下是加密參數說明。
參數
說明
鹽salt
設定為Token尾碼,本樣本中為4f5ff3ce。
迭代次數iteration
設定為:10000
輸出位元組長度output len
設定為:50
加密金鑰長度key size
設定為:256
輸出類型
設定為:Hex
搜尋常用工具網站進行解密,例如charsetpbkdf2。
Java語言解密。
package main import javax.crypto.SecretKeyFactory; import java.security.GeneralSecurityException; import javax.crypto.spec.PBEKeySpec; import java.security.spec.KeySpec; public class PBKDFExapmle { public static void main(String[] args) { String password = "yV9H****jNKkvKoLMiypOc5T0OovHXPV"; String salt = "4f5ff3ce"; int iterationCount = 10000; int outputLength = 50 * 8; try { KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), iterationCount, outputLength); SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); byte[] keyBytes = skf.generateSecret(spec).getEncoded(); System.out.println(bytesToHex(keyBytes)); } catch (GeneralSecurityException e) { e.printStackTrace(); } } private static String bytesToHex(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(0xff & b); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } }
輸出:PBKDF2 Password
c3cd****971bab928e4ecd6e7a00c74657696ea07d38c43f3bb5dc3190f2285cb80695cf7bf2f25c9b1f34fe1e0f9549****
Go語言加密。
package main import "fmt" import "encoding/hex" import "crypto/sha256" import "golang.org/x/crypto/pbkdf2" func main() { password:="yV9H****jNKkvKoLMiypOc5T0OovHXPV" salt="4f5ff3ce" newPasswd := pbkdf2.Key([]byte(password), []byte(salt), 10000, 50, sha256.New) encodePassword:= hex.EncodeToString(newPasswd) fmt.Println(encodePassword) }
輸出:PBKDF2 Password
c3cd****971bab928e4ecd6e7a00c74657696ea07d38c43f3bb5dc3190f2285cb80695cf7bf2f25c9b1f34fe1e0f9549****
基於PBKDF2加密後的資訊構造MD5摘要簽名參數。
MD5資訊摘要演算法(MD5 Message-Digest Algorithm),是一種被廣泛使用的密碼散列函數,可以產出一個128位(16位元組)的散列值(hash value),用於確保資訊傳輸完整一致。
加密內容為:<PBKDF2 Password>+"_"+<當前系統時間整數,單位:秒>
例如:
PBKDF2 Password為:1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****
當前系統時間為2024-09-20 17:12:13 ,則精確到秒的時間整數為:1726823533
所以需要MD5簽名的整體資訊為:1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****_1726823533
MD5簽名有多種方式:
搜尋常用網站工具加密,例如MD5。
Java代碼進行MD5簽名。
package main; import java.security.MessageDigest; public class MD5 { public static void main(String[] args) { String pbkdfPassword = "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****"; long timeSeconds=System.currentTimeMillis()/1000; String key=pbkdfPassword+"_"+timeSeconds; System.out.println(MD5.getMD5String(key,"UTF-8")); } public static String getMD5String(String str, String charset) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.reset(); messageDigest.update(str.getBytes(charset)); byte[] byteArray = messageDigest.digest(); StringBuffer md5StrBuff = new StringBuffer(); for (int i = 0; i < byteArray.length; i++) { if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) { md5StrBuff.append("0").append( Integer.toHexString(0xFF & byteArray[i])); } else { md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); } } return md5StrBuff.toString().toLowerCase(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("MD5 error:"+e.getMessage()); } } }
Go代碼進行MD5簽名。
package main import ( "crypto/md5" "encoding/hex" "fmt" "io" "time" "strconv" ) func main() { // 需要計算MD5的字串 pbkdfPassword := "1e5b****80184e78832544aae4d2e031a3539c10b575b75d7c1d44af49fcf5a7de9c58a5f0035ce35fff0e5b0476e882****" timeSeconds:= time.Now().Unix() key:=pbkdfPassword+"_"+strconv.FormatInt(timeSeconds, 10) // 使用MD5包計算字串的MD5值 hash := md5.New() io.WriteString(hash, key) md5Str := hash.Sum(nil) // 將二進位的MD5值轉換成十六進位字串 md5StrHex := hex.EncodeToString(md5Str) fmt.Println("MD5 of", key, "is", md5StrHex) }
基於產生的MD5簽名資訊拼接連結參數。
參數說明:
參數名
說明
樣本
aliyun_api_key_sign
MD5簽名,隨時間變化。
例如:c3bf89b867cc88df72d507edc4d1****
aliyun_api_key_timestamp
簽章時間,當簽章時間和系統時間超過1分鐘後,簽名到期失效。
例如:1726823533
aliyun_api_key_name
Service Account Token的名稱。
例如:test1
aliyun_api_key_org_id
Service Account Token所在的組織Org ID。
例如:1
aliyun_api_key_expire_seconds
MD5簽名登入後失效時間,單位:秒。
預設:3600
樣本如下:
https://grafana-example.grafana.aliyuncs.com/d/TZWea****/test?orgId=1&from=167081684****&to=167083844****&aliyun_api_key_sign=c3bf89b867cc88df72d507edc4d1****&aliyun_api_key_timestamp=1726823533&aliyun_api_key_name=test1&aliyun_api_key_org_id=1
至此您可以基於程式碼每次動態產生更安全的免密登入Grafana的連結,同時可以避免Service Account Token泄露問題。
常見問題
通過共用串連訪問大盤時頁面報錯如下:
可能原因:內嵌大盤情況下,allow_embedding參數未設定。配置allow_embedding參數的操作,請參見上文步驟一。
無法顯示大盤資料。
內嵌大盤情況下,Cookie無法寫入導致,可能原因如下:
跨域,即根網域名稱不同時,預設配置無法寫入Cookie。
cookie_samesite參數設定為none,但是cookie_secure參數設定為false。
網域名稱為HTTP。由於cookie_secure參數無法在HTTP下生效,因此網域名稱不支援HTTP。
解決方案:參考上文步驟一重新設定Grafana參數。
通過共用串連訪問大盤時頁面報錯如下:
可能原因:
瀏覽器版本過低。
內嵌大盤的情況下瀏覽器配置導致。
解決方案:
查看Cookie使用的配置,允許使用Cookie。
若使用Chrome瀏覽器,在無痕模式下需要配置允許所有Cookie。
如果用於大盤內嵌,API Key建議是設定較短的有效時間使用一次就更換,還是配置一個很長時間的免登Key?
您可以根據安全需要做配置,建議3個月換一次,若Key泄露可以通過刪除讓Key失效。
API Key是否有數量上限?
Key的建立官方源碼裡並沒有做限制,由於頁面查詢時最多展示100條,建議不要超過100個。
API Key配置的有效時間到期了,這個API Key會自動刪除嗎?
Key失效後,資料仍然存在,如果擔心佔用數量您可以手動刪除Key。Grafana頁面上失效的Key預設不展示,您可以單擊Include expired keys顯示已失效Key,然後刪除。