您的位置 首页 > 体育运动

【fmh15球员】istio 1.10学习笔记14: 使用istio实现http服务的JWT身份认证

前面一节初步学习了istio安全管理功能中的认证策略,并使用认证策略配置了服务之间的双向TLS,使用认证策略对暴露到集群外部的http服务开启了基于JWT的终端用户认证。本节将对上节配置JWT终端用户认证时用到一些JWT相关知识做一个补充学习。

JWT即JSON Web Token,是一种用于产生访问令牌的开放标准(rfc7519)。 JWT token被设计为紧凑且安全的,特别适用于实现分布式服务的认证方案,适用于实现跨域认证方案。

JWKS(JSON Web Key Set)和JWK(JSON Web Key)

之前在使用istio配置JWT终端用户认证时,创建的认证策略中用到了jwksUri字段:

apiVersion: kind: RequestAuthentication metadata: name: "jwt-example" namespace: istio-system spec: selector: matchLabels: istio: ingressgateway jwtRules: - issuer: "testing@; jwksUri: "; EOF

JWKS是一个json文件:

{ "keys":[ { "e":"AQAB", "kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ", "kty":"RSA", "n":"xAE7eB6qugXyCAG3yhh7pkD..." } ] }

JWKS即JWK Set,也就是一组JWK。那么什么是JWK呢?JWK即JSON Web Key是JWT的秘钥,描述了一个加密密钥(公钥或私钥)的值及其各项属性。 JWKS就是一组JWK密钥。Istio就是使用JWKS提供的密钥信息对JWT token进行签名验证的。JWKS的JSON文件格式如下:

{ "keys": [ <jwk1>, <jwk2>, ... ]}

使用jwx命令行工具生成JWK

那么怎么生成JWK呢?这里使用这个命令行工具。 jwx是一个用go语言开发的命令行工具,内置了对各种JWx(JWT, JWK, JWA, JWS, JWE)的支持。

使用go get安装jwx命令行工具:

go get -u -v gi

下面演示使用jwx命令行工具生成一个JWK,通过模板指定kid为myawesomekey:

jwx jwk generate --keysize 4096 --type RSA --template '{"kid":"myawesomekey"}' -o r cat r { "d": "grRsO6jTvTun5cnpBNf...", "dp": "8RUd5gEJAqxo7kMqdEFkeQu3....", "dq": "SQ2Xh8iou8Qd-THxPXKfTJDnI...", "e": "AQAB", "kid": "myawesomekey", "kty": "RSA", "n": "wIUzbQ8-WJ_u4rmDAIASBLODKlR7TMBYo...", "p": "9Ka47wKNibYmGtImrbXeLkbSngUaWXMWf2...", "q": "yXNqB6DeuFWWy9eyb_Ab6VaqdeeqclmpHT...", "qi": "hd4_FexqN4rpbihDRGmG_WdnE4Uwnyuqv..." }

从r中提取JWK公钥:

jwx jwk fmt --public-key -o r r cat r { "e": "AQAB", "kid": "myawesomekey", "kty": "RSA", "n": "sptztCJ....." }

上面生成的JWK其实就是RSA公钥私钥地换了一种存储格式而已,下面演示如何将它们转换成PEM格式的公钥和私钥:

jwx jwk fmt -I json -O pem r -----BEGIN PRIVATE KEY----- MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCym3O0Ik5QGZ8i ...... -----END PRIVATE KEY----- jwx jwk fmt -I json -O pem r -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsptztCJOUBmfIqSE8LR5 ...... -----END PUBLIC KEY-----

使用jwx命令行签发JWT Token并验证有效性

签发一个JWT Token:

jwx jws sign --key r --alg RS256 --header '{"typ":"JWT"}' -o - <<EOF { "iss": "testing@;, "sub": "john007", "iat": 1628138793, "exp": 1629138793, "name": "John Doe" } EOF cat eyJhbGciOiJSUzI1NiIsImtpZCI6Im15YXdlc29tZWtleSIsInR5cCI6IkpXVCJ9......

上面生成JWT Token看起来是这样的:

eyJhbGU省略部分字符CI6Ik省略部分字符省略部分字符WVvVGhNvkhXs

实际上是由下面的算法生成的:

base64url_encode(Header) + '.' + base64url_encode(Claims) + '.' + base64url_encode(Signature)

可以使用jwx命令行工具将jwt token中的header, claims(payload), signature解析出来:

jwx jws parse Signature: "jUgIDUhHJ4A..." Protected Headers: "eyJhbGciOiJSUzI1..." Decoded Protected Headers: { "alg": "RS256", "kid": "myawesomekey", "typ": "JWT" } Payload: { "iss": "testing@;, "sub": "john007", "iat": 1628138793, "exp": 1629138793, "name": "John Doe" }

先看一下Headers部分,包含了一些元数据,注意type和alg是必须的:

  • type: token的类型,JWT表示JWT类型的token
  • alg: 所使用的签名算法,这里是RSA256
  • kid: JWK的kid

再看一下Payload(Claims)部分,payload包含了这个token的数据信息,JWT标准规定了一些字段,另外还可以加入一些承载额外信息的字段。

  • iss: issuer,token是谁签发的
  • sub: token的主体信息,一般设置为token代表用户身份的唯一id或唯一用户名
  • exp: token过期时间,Unix时间戳格式
  • iat: token 创建时间, Unix时间戳格式
  • jti: 当前token的唯一标识

最后看一下签名Signature信息,签名是基于JSON Web Signature (JWS)标准来生成的,签名主要用于验证token是否有效,是否被篡改。 签名支持很多种算法,这里使用的是RSASHA256,具体的签名算法如下:

RSASHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), <rsa-public-key>, <rsa-private-key>

最后使用RSA Public Key验证JWT Token的有效性:

jwx jws verify --alg RS256 --key r { "iss": "testing@;, "sub": "john007", "iat": 1628138793, "exp": 1629138793, "name": "John Doe" }

在istio认证策略中配置JWT终端用户认证

有了前面对JWT,JWK,JWKS概念的学习,下面配置istio的认证策略使用我们自己创建的JWKS。

apiVersion: "" kind: "RequestAuthentication" metadata: name: "jwt-example" namespace: istio-system spec: selector: matchLabels: istio: ingressgateway jwtRules: - issuer: "testing@; forwardOriginalToken: true jwks: | { "keys": [ { "e": "AQAB", "kid": "myawesomekey", "kty": "RSA", "n": "tZdEjpwtPlRHRFqdUGX6zgCht0rT5hrbs1iXzHKJ8XIJiqgCS6HYCSR9F8ziHfW5fmhftGHmmhQxw1eXou0olIcSGHQsLjkravorvr6vMcNU4OmX48CZzVxhUMBWStbNhHOlOCMKxPnHmT4tlyuv9CwgQ0zI8_qlCpWKhlSVjTo92VRVVFHnhMAS0garwIyHmv_nHLovrdFz9fQtHPQwxvEolylnCfPANGhNJsdYbMl-7lONyB7gb_ymrc7ykt372cYEBIF419b-MIT9Sl7_hL_e4Qyw565pbipqGUDhXHSiDpTI_8Y8gK-Gev7VZu_T-BBME9VO0n00gSHIASUlw-EISnvSFrNUXCi05RN2EfH48Elc0Og9eL5OHxrXLQV6FG4pPBiR2Umx7a4rqp5X5fjbCVe0lRYL0kU7neW02n3UWItbF1oSFLL4SPQmRTMZBrECcxiq3RF51pPwBM-qAGH8KAxY3tgO_xefe8Wd3BSFZHFF_xiEGb2G0W43osFStPKQ0e7TvX2FVlP7XSpQ3Ym9FrRfKolEY7bQv2YpeCjOycFMVUR1geazEiVEJbwCrJIMQPbTI9S3_gyOBknoR34kiY52nJeLgA_jwneS4yWLVi6OESZjrgF12Kjh5Y78gwQEQD0EHJ1T9VQrdDOWtvqnK2H6HF3vcr1XyxpruQs" } ] }

上面的RequestAuthentication中的jwtRules中使用了jwks字段配置了我们前面创建的JWK公钥。forwardOriginalToken: true表示启用Authorization请求头转发,默认情况下,istio在完成了身份验证之后,会在转发的请求中移除Authorization请求头,为了确保后端服务获取到终端用户信息,可以设置forwardOriginalToken为true。另外注意到配置里的jwtRules是数组,可以配置多个jwtRule,实际使用中会为不同的jwt issuer配置自己的jwks,每个jwks中有多个jwk。JWK认证规则是根据JWT token payload中的iss字段匹配到istio中配置的jwks,然后根据JWT token header中的kid字段在jwks中找到对应的jwk公钥,使用找到的jwk公钥对token验证签名合法性。

上节内容我们还学习到RequestAuthentication配置的认证策略,默认情况下会忽略不带Authorization请求头的流量直接放行,需要配置授权策略AuthorizationPolicy要求必须携带Authorization请求头。

apiVersion: kind: AuthorizationPolicy metadata: name: "frontend-ingress" namespace: istio-system spec: selector: matchLabels: istio: ingressgateway action: DENY rules: - from: - source: notRequestPrincipals: ["*"] to: - operation: hosts: [";]

下面使用前面创建的jwk私钥签发一个token,确认使用新签发的token可以访问:

jwx jws sign --key r --alg RS256 --header '{"typ":"JWT"}' -o - <<EOF { "iss": "testing@;, "sub": "john007", "iat": 1628138793, "exp": 1629138793, "name": "John Doe" } EOF TOKEN=$(cat ) && \ curl --header "Authorization: Bearer $TOKEN" -s -o /dev/null -w "%{http_code}\n" 200 curl --header "Authorization: Bearer errortoken" -s -o /dev/null -w "%{http_code}\n" 401 curl -s -o /dev/null -w "%{http_code}\n" 403

注意上面的测试中,使用签发的合法的token,可以正常访问。 使用错误的token,即RequestsAuthentication验证失败的请求,会返回401状态码。 不带token的请求,即AuthorizationPolicy验证失败的请求,会返回403状态码。

参考




关于作者: luda

无忧经验小编鲁达,内容侵删请Email至wohenlihai#qq.com(#改为@)

热门推荐