Недавно я включил IAP в кластере GKE.
- Версия кластера: 1.15.11-gke.11
Я следовал инструкциям здесь: https://cloud.google.com/iap/docs/enabling-kubernetes-howto
Сервис config выглядит следующим образом:
---
apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
name: foo-bc-iap
namespace: foo-test
spec:
iap:
enabled: true
oauthclientCredentials:
secretName: iap-client-secret
---
apiVersion: v1
kind: Service
metadata:
name: foo-internal-service
namespace: foo-test
annotations:
cloud.google.com/backend-config: '{"ports":{"80":"foo-bc-iap"}}'
spec:
type: NodePort # To create Ingress using the service.
selector:
app: foo-test
ports:
- protocol: TCP
port: 80
targetPort: 8081
В качестве учетных данных я использовал идентификатор клиента OAuth 2.0 (тип: веб-приложение).
После того, как я убедился, что конечная точка API, защищенная IAP, работает иначе, когда я активирую IAP для службы Kubernetes, я написал следующую тестовую программу, чтобы убедиться, что конечная точка доступна из учетной записи службы, указанной в JSON файле 'account. json'.
При написании этого примера приложения я консультировался с этим do c: https://cloud.google.com/iap/docs/authentication-howto#iap_make_request - go
- google. golang .org / api v0.23.0
- go 1.12
func (m *myApp) testAuthz(ctx *cli.Context) error {
audience := "<The client ID of the credential mentioned above>"
serviceAccountOption := idtoken.WithCredentialsFile("account.json")
client, err := idtoken.NewClient(ctx.Context, audience, serviceAccountOption)
if err != nil {
return fmt.Errorf("idtoken.NewClient: %v", err)
}
requestBody := `{
<some JSON payload>
}`
request, err := http.NewRequest("POST", "https://my.iap.protected/endpoint",
bytes.NewBuffer([]byte(requestBody)))
if err != nil {
return fmt.Errorf("http.NewRequest: %v", err)
}
request.Header.Add("Content-Type", "application/json")
response, err := client.Do(request)
if err != nil {
return fmt.Errorf("client.Do: %v", err)
}
defer response.Body.Close()
fmt.Printf("request header = %#v\n", response.Request.Header)
fmt.Printf("response header = %#v\n", response.Header)
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return fmt.Errorf("ioutil.ReadAll: %v", err)
}
fmt.Printf("%d: %s\n", response.StatusCode, string(body))
return nil
}
Но когда я запустил это, я смог увидеть только следующий ответ.
request header = http.Header{"Authorization":[]string{"Bearer <jwt token>"}, "Content-Type":[]string{"application/json"}, "X-Cloud-Trace-Context":[]string{"c855757f20d155da1140fad1508ae3e5/17413578722158830486;o=0"}}
response header = http.Header{"Alt-Svc":[]string{"clear"}, "Content-Length":[]string{"49"}, "Content-Type":[]string{"text/html; charset=UTF-8"}, "Date":[]string{"Wed, 06 May 2020 22:17:43 GMT"}, "X-Goog-Iap-Generated-Response":[]string{"true"}}
401: Invalid IAP credentials: JWT signature is invalid
Как вы можете видеть здесь, в доступе было отказано .
Итак, я подумал, что подпись, используемая для подписи токена JWT в заголовке, может быть неправильной.
Но с помощью jwt.io я убедился, что следующее:
- Токен JWT, используемый в заголовке, подписан закрытым ключом учетной записи службы вызывающего абонента
- Токен JWT, используемый в заголовке, можно проверить с помощью ключа publi c учетной записи службы вызывающего абонента
- Токен JWT был подписан с использованием алгоритма RS256
И я также изучил токен:
{
"alg": "RS256",
"typ": "JWT",
"kid": "<the service account's private key ID>"
}
{
"iss": "<email address of the service account>",
"aud": "",
"exp": 1588806087,
"iat": 1588802487,
"sub": "<email address of the service acocunt>"
}
Ничего особенного.
Так что я не уверен, что здесь происходит. Если я отключу IAP, конечная точка вернет правильный ответ .
Может ли кто-нибудь подсказать мне, что я делаю неправильно?