本文主要介绍如何在istio中,通过使用jwt为应用进行授权,实现权限访问。

本文实战代码环境:k8s(1.19)+istio(1.8.0)+python(3.7)/js(node v10.24.0)

实战内容:用户调用接口生成jwk(代码实战),调用接口生成token(代码实战),并通过token访问应用。

1、什么是JWK,JWKS,JWT?

jwt:json web token,访问网站或请求时,用来证明自己身份的一种凭证。

jwk:json web key,注意这个key,可以理解为一把钥匙,实际上就是去验证jwt的东西。

jwks:json web token key set ,不难理解,JWKS 描述一组 JWK 密钥

所以,Istio 使用 JWK 描述验证 JWT 签名所需要的信息。在使用 RSA 签名算法时,JWK 描述的应该是用于验证的 RSA 公钥。

2、怎么验证各方身份?

一个通常你看到的jwt,由以下三部分组成,它们分别是:

header:主要声明了JWT的签名算法;
payload:主要承载了各种声明并传递明文数据;
signture:拥有该部分的JWT被称为JWS,也就是签了名的JWS;没有该部分的JWT被称为nonsecure JWT也就是不安全的JWT,此时header中声明的签名算法为none。
三个部分用 英文句点分割,可以使用shell 命令

 cut -d . -f 1 |base64 -d

直接查看里面的内容。

istio中,除了jws的验证之外,更关键的地方在于不仅要验证签名,还要验证你的iss和sub,而什么是iss和sub?

iss 【issuer】发布者的url地址
sub 【subject】该JWT所面向的用户,用于处理特定应用
aud 【audience】接受者的url地址
exp 【expiration】 该jwt销毁的时间;unix时间戳
nbf 【not before】 该jwt的使用时间不能早于该时间;unix时间戳
iat 【issued at】 该jwt的发布时间;unix 时间戳
jti 【JWT ID】 该jwt的唯一ID编号

所以,Istio 在接收到用户的请求jwt后,根据header获取你的加密算法,然后解析你的payload,根据你提供的签名,对token进行验证。验证时,在签名正确的基础上,会去判断你的应用的发布者、使用者是否与应用token中所携带的一致,不一致时,则拒绝用户请求。

3、实战 

具体的流程大致如下:

其中包含了几个环节:istio鉴权请求发布、istio鉴权策略发布、jwk生成、jwt生成、用户请求。我们将根据涉及到的环节来逐步介绍。

(1)istio鉴权请求发布

 istio中,如果使用jwt鉴权,需要完成两步,第一步便是配置RequestAuthentication,具体官方文档可参考:https://istio.io/latest/docs/tasks/security/authorization/authz-jwt/

 本案例中,istio的RequestAuthentication的配置如下:

apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
  name: sklearn-jwt-example
  namespace: test
spec:
  jwtRules:
  - issuer: testing@istio.io
    jwksUri: https://raw.githubusercontent.com/istio/istio/release-1.8/security/tools/jwt/samples/jwks.json

具体来说:在test的ns中的所有istio sidecar,凡是token发布者为testing@istio.io的鉴权都生效,同时需要去请求jwkuri获取jwk内容,而这里获取的jwk则是前面所说到用来验证签名的内容,但实际上,jwksURL这个请求访问起来有困难,容易到时后期鉴权时出现jwt认证出现失败的情况,所以这就为jwk的生成提供了进一步的需求。

(2)jwk生成

要生成 JWK 公钥,需要先生成私钥,下面是一个简单的生成命令。

openssl genrsa -out key.pem 2048

生成的key.pem只是私钥,不能用,且需要转换为jwk(公钥的另一种表达方式)格式,这里介绍两种,python:

# 需要先安装依赖: pip install jwcrypto
from jwcrypto.jwk import JWK
from pathlib import Path

private_key = Path("key.pem").read_bytes()
jwk = JWK.from_pem(private_key)

# 导出公钥 RSA Public Key
public_key = jwk.public().export_to_pem()
print(public_key)

print("="*30)

# 导出 JWK
jwk_bytes = jwk.public().export()
print(jwk_bytes)

javascript:

const fs = require('fs');
const jose  = require('jose');#需要安装jose ,版本为2.0.2 yarn add jose@2.0.2
const privateKey = fs.readFileSync('key.pem');
const privateJwk = jose.JWK.asKey(privateKey).toJWK();
console.log(privateJwk)

之后,便可以通过express或者flask等框架,快速发布的到服务器中,这样就实现了jwk的生成

(3)istio鉴权策略发布

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: require-jwt
  namespace: test
spec:
  selector:
    matchLabels:
      abc: bcd
  action: ALLOW
  rules:
  - from:
    - source:
       requestPrincipals: ["testing@istio.io/appID"]

这里面,有几个需要注意的点,matchlabel是为了能够将你的策略配置到对应istio应用上,使得整个策略生效,而requestPrincipals这表示,你这个策略的令牌对应的istio应用是由testing@istio.io颁发给appID(这个很重要)的。到此为止,我们似乎就在k8s中,配置好了istio中所需要的一切,现在就是如何访问了。

(4)jwt生成

访问时,需要携带jwt,所以,如何生成jwt,成为关键。给出python和js的代码如下:

python:https://github.com/istio/istio/blob/release-1.8/security/tools/jwt/samples/gen-jwt.py

python可以参考这个istio官方代码,就不贴出来了

js:

const jwt = require('jsonwebtoken');
const fs = require('fs');
const privateKey = fs.readFileSync('key.pem');
const token = jwt.sign(
      { "userinfo":"用户自定义" },
      privateKey,
      {
        algorithm: 'RS256',
        expiresIn: '8760h', // 1 years
        issuer: 'testing@istio.io',
        subject: "appID",
      },
    );
console.log(token)

代码中,可以看到对issuer和subject做了定义,这也就是和istio的yaml中对应上了。需要说明的是,用户自定义内容在JWT中,不应该在载荷里面加入任何敏感的数据。像密码这样的内容就不能被放在JWT中了。如果将用户的密码放在了JWT中,那么怀有恶意的第三方通过Base64解码就能很快地知道你的密码了。

(5)用户请求

激动人心的一步,你只需要拿着你的token就可以发送请求访问请求了,具体方法,在postman中,可在header中添加autority信息,或者在authorization中添加bearer token即可。

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐