英文:
Prevent Cloud Run from passing on Authorization header
问题
我在Cloud Run上托管了Grafana。Grafana启用了匿名访问,而Cloud Run需要有效的凭据。
现在,我试图模仿https://grafana.com/docs/grafana/latest/http_api/dashboard/中的第一个示例。因此,我将一个服务帐号密钥下载到/tmp/creds.json
。我将这个文件设置为GOOGLE_APPLICATION_CREDENTIALS
。
然后,我使用这些凭据向/api/dashboards/db
发送一个POST请求。
我看到的情况是,我通过Google身份验证,它启动了Cloud Run实例,然后我从Grafana收到了一个错误消息:
{"message":"invalid API key"}
这让我相信Google身份验证没有使用Authorization
头,而是将其委托给其他地方。
所以我的问题是,如何阻止Google身份验证委托这个头部?
或者作为一种解决方法,如何让Grafana忽略这个头部?
这是我用来发送请求的源代码:
func TestCall(t *testing.T) {
r := strings.NewReader(`
{
"dashboard": {
"id": null,
"uid": null,
"title": "Production Overview",
"tags": [ "templated" ],
"timezone": "browser",
"schemaVersion": 16,
"version": 0,
"refresh": "25s"
},
"folderId": 0,
"folderUid": "l3KqBxCMz",
"message": "Made changes to xyz",
"overwrite": false
}
`)
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/tmp/creds.json")
ctx := context.TODO()
ts, err := idtoken.NewTokenSource(ctx, "https://grafana-123.run.app")
if err != nil {
if !strings.HasPrefix(err.Error(), "idtoken: credential must be service_account") {
panic(err)
}
creds, err := google.FindDefaultCredentials(ctx)
if err != nil {
panic(err)
}
ts = creds.TokenSource
}
tok, err := ts.Token()
if err != nil {
panic(err)
}
if tok.AccessToken == "" {
panic("Empty token!")
}
fmt.Printf("AT %s\n", tok.AccessToken)
client := &http.Client{}
req, err := http.NewRequest("POST", "https://grafana-123.run.app/api/dashboards/db", r)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+tok.AccessToken)
// This does not work at all: req.Header.Set("Proxy-Authorization", "Bearer "+tok.AccessToken)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("SC %d\n", resp.StatusCode)
bs, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
panic(string(bs))
}
}
英文:
I have Grafana hosted on Cloud Run. Grafana has anonymous access enabled while Cloud Run needs valid credentials.
Now I try mimic the first example of https://grafana.com/docs/grafana/latest/http_api/dashboard/. Thus I downloaded a service account key to /tmp/creds.json
. This file I set as GOOGLE_APPLICATION_CREDENTIALS
.
Using these credentials I then send a POST to /api/dashboards/db
.
What I see is that I get through Google auth, it starts up Cloud Run instances and I get an error message from Grafana:
{"message":"invalid API key"}
This in turn then lets me believe that Google auth is not consuming the Authorization
header but delegating it along.
So my question is how can I prevent Google auth from delegate this header?
Or as a workaround how can I make Grafana ignore the header?
Here the source I use to make the requests:
func TestCall(t *testing.T) {
r := strings.NewReader(`
{
"dashboard": {
"id": null,
"uid": null,
"title": "Production Overview",
"tags": [ "templated" ],
"timezone": "browser",
"schemaVersion": 16,
"version": 0,
"refresh": "25s"
},
"folderId": 0,
"folderUid": "l3KqBxCMz",
"message": "Made changes to xyz",
"overwrite": false
}
`)
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "/tmp/creds.json")
ctx := context.TODO()
ts, err := idtoken.NewTokenSource(ctx, "https://grafana-123.run.app")
if err != nil {
if !strings.HasPrefix(err.Error(), "idtoken: credential must be service_account") {
panic(err)
}
creds, err := google.FindDefaultCredentials(ctx)
if err != nil {
panic(err)
}
ts = creds.TokenSource
}
tok, err := ts.Token()
if err != nil {
panic(err)
}
if tok.AccessToken == "" {
panic("Empty token!")
}
fmt.Printf("AT %s\n", tok.AccessToken)
client := &http.Client{}
req, err := http.NewRequest("POST", "https://grafana-123.run.app/api/dashboards/db", r)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+tok.AccessToken)
// This does not work at all: req.Header.Set("Proxy-Authorization", "Bearer "+tok.AccessToken)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("SC %d\n", resp.StatusCode)
bs, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
panic(string(bs))
}
}
答案1
得分: 1
头部的Proxy-Authorization应该是有效的,但我也没有成功。
为了澄清你的问题,你正在使用IAP(OIDC身份令牌)来授权访问Cloud Run。你的软件(Grafana)正在处理HTTP授权头部,并假设你传递的是一个API密钥(但失败了)。我对Grafana的内部机制不太熟悉,但一个可能的解决方案是编写一个中间件,检测到OIDC令牌并删除该头部。我不知道Cloud Run是否有一种方法可以从你的应用程序接收到的HTTP请求中删除授权头部。
在我看来,这是一个应该在Grafana代码中解决的问题。如果Grafana启用了匿名访问,那么它就不应该验证HTTP授权头部。考虑向Grafana提交一个问题报告,或修复代码并提交一个补丁。
英文:
The header Proxy-Authorization should work, but I have also been unsuccessful.
To clarify your problem, you are using IAP (OIDC Identity Token) to authorize access to Cloud Run. Your software (Grafana) is processing the HTTP Authorization header and assuming that you are passing it an API Key (which fails). I am not familiar with Grafana internals, but one possible solution is to write middleware that detects an OIDC token and removes that header. I am not aware of a Cloud Run method to remove the Authorization header from HTTP requests that your application receives.
IMHO this is a problem that should be resolved in Grafana's code. If Grafana has anonymous access enabled, then it should not be validating the HTTP Authorization header. Consider opening an issue with Grafana or fixing the code and submitting a patch.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论