在前一篇笔记中我们验证了使用sa的token作为一种认证,向apiserver发送请求,这里简述下它的认证原理和流程。

首先得知道这种token称为JWT(json web token),可以参考官网介绍,而且这是一种RFC标准。

JWT的工作原理

JWT是服务端发给客户端的一种加密凭证(通过RSA或者密码加密),客户端访问服务端(此不一定是发布凭证的服务端)时携带上这个凭证,服务端解密此凭证,验证通过就可以允许客户端访问。

在k8s中,使用RSA私钥/公钥进行加密和验证,kube-controller-manager,使用如下参数指定私钥,对token进行签名

--service-account-private-key-file = /etc/kubernets/pki/sa.key

kube-apiserver使用如下参数指定公钥,对token进行验证

--service-account-key-file = /etc/kubernets/pki/sa.pub

a. JWT结构

JWT包含三部分,分别为: Header,Payload,Signature,之间用"."分隔,所以一般形式为xxx.yyy.zzz。
header
一般包含两部分, typ指定了类型,固定为"JWT",alg指定了签名算法, 比如HMAC SHA256 or RSA。

{
"alg": "HS256",
"typ": "JWT"
}
header需要使用Base64Url 加密后,作为JWT的第一部分

payload
定义了用户数据,有定义好的知名的字段,也可以自定义字段

{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

payload需要使用Base64Url 加密后,作为JWT的第二部分

Signature
签名这一步需要四个条件:Base64Url 加密后的header,Base64Url 加密后的paload,secret(可以是密码,也可以是RSA的私钥)和header中指定的加密算法。比如使用 RSASHA256 算法计算签名的公式如下:

RSASHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

上面公式执行时,大概分为两步:

a. 使用算法 RSASHA256 对 base64UrlEncode(header) + "." +
base64UrlEncode(payload) 部分计算出一个 32 位的hash值
b. 使用secret(比如RSA的私钥)对 32 位的hash值进行签名,得出一个签名后的值
Signature

b. 生成JWT

最后将这三部分(都要经过base64加密)使用"."结合起来就是最终的token

base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + base64UrlEncode(Signature)

c. 验证JWT

服务端收到token后,根据"."将三部分解析出来,
使用base64 decode 第一和第二部分,可以得到header和payload,使用header中指定的算法计算出一个 新 hash 值,
使用base64 decode 第三部分,得到签名值Signature, 再使用RSA公钥对Signature解密得到原始 hash 值。
只要新 hash 和原始 hash一致就说明JWT是有效的。

生成/验证JWT

上面只是简述原理和流程,这里实践如何生成JWT,如何验证JWT。
有两种方法来生成/验证JWT
a. 使用 jwt.io 官方提供的图形界面
b. 使用第三方库,比如python中的jwt (pip install python-jwt 通过此命令安装)

a. jwt.io

生成JWT
在右侧的decoded下面的三个框分别填写header,payload和VERIFY SIGNATURE中的private key(对于k8s来说,私钥就是/etc/kubernetes/pkt/sa.key),最上面的Algorithm选择签名算法,这里选择rs256. 会自动在左侧的encoded框显示出生成的JWT,如下图

image.png

解密JWT
把JWT填写到左侧的encoded中,系统会自动识别出header和payload,因为他俩是base64加密的,直接解密即可,但是签名部分需要提供公钥才能进行验证,否则encoded框下面会显示红色的"Invalid Signature",只要把公钥(对于k8s来说,公钥就是/etc/kubernetes/pkt/sa.pub),填写到右侧decoded下面的VERIFY SIGNATURE框的public key处即可验证,如下图
没填写公钥时

image.png

填写正确的公钥后

image.png

b. python jwt 库

如下是python脚本,使用jwt库生成JWT,并验证JWT

root@master:~/token# cat test.py
#!/bin/env pythonimport jwt
#私钥内容 sa.key,一定要是完整的私钥,包括前面的"-----BEGIN"和后面的"KEY-----"
private_key = b'-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAwojpJBO8QZla+3sfdYji4aXXPesNAI9ScNOpyP4mcFLNxDOQluf7TBIQfzGC7Ynk58TXv9KeC4jO8FK2UsyjF9bKf8O3j3EhO+t8zNUD35vsqovTFyr1TvvjA8MMaH51lRIvKIQma58li4yh844lauwR4O7zAnd0RIg7ib7WbwCtkMw7RPdliy84CLsHdxSh4TZ1uWkiVb8eF5WT/28PnL5VwVHhg3A2v+JVBt4Fd9aNjanMBxIq5Tt0hlGOEmc7rCUmf/WFQz4Ddw54ZYQ+cD7Y9HwTqzCWMW14YbYNmohzn/fNpX5vXevZhX8aFjcgTYW5PX8jXAbBOcjWLtsXSwIDAQABAoIBADTpCghO+dgZvt5Beaf9KEBZW/ayVKH/WVvopfhN7+SDEQY5RC1XQUlKbIQ70jGLXOAQ8OFyhpv6hNZmmMJieEWGnSMs92MjUPe8MACCO4B5J2CnkS1u+LOX3QHr7hcJti9qd7scXlrNOWgAQxg8ZD71oFM+iof0N4JgT1lCt44O9rgmQ0lXNz1R46zB4mU4V5K0qgiX/42UMZs2N0HeRQ3Fx0uP0qGdCIL9N1DSMhvmNFHqBN6wl9bs0tBrvX32l0MNaWdJccLo33fzWE3HUlaHZttETL2p94UCAcuagIb7RT+79W960w0yFPuM1wodTcsM25uo7lglAhQoMccPTKECgYEA2dDfVj6zU0aRNsmOmMpDtXxUz0a5dJDIFu5SHmekgwiPd0rQR1WmnJRt9TUILvJsAxLIw3935DSIt1akK4lppohcmApHK6/B2TA792YF0CFMVscVA9F2QaA4hlpFUsNRDlF43wzhW31spmDfYazLc7V9OZF8ZaXdroJoU2u7xRsCgYEA5KM7C6necEXkwF1wuIIB18UPMgNEPFTJxWgkaqmQSML2SEaxp3xsaAg+RNGgfBVNlZR7OapwFnhVMI0t6Q1vMim74f4XlBh3dqB1D+He6YGpqN95ElI+Ck1s+U0/wQm+iZWn47t3UA2E9NFimiqY0JgNdV317iN9/dTxshhfiZECgYBeW+0UqK746X4pFOIQcLcqXQVEkifvRnVX8cBjaZTMKx4zmJZoAMPf2zFTY7j61YxTPIT6pDLlCpkbi44tSicZvMMYHoO8ejRpCUtBHtJv2qz+ftoswEYRof46vcqAUxq/MC5Duom6H7i8zwSWhMvSgZIRKWSRiGxjmBzn3qkGdwKBgCZnRIOxBKvXEZU+HLDhJW4Yq3S7F7sKgtmlpHhGAvY1yShat3xqacsPl2X3z/0HlwCI8Cm/dxRPIgAFtrBukT7bw7Mx+sPlWCuUyBTi245dOSIkZzGsnr8cQjGdyBeki1yQxqJ52pCXtL1qbiV3AjQHVjtgjO5zB7abDf3cGjABAoGAUQ2jfqkKKV+tW4vl1i1HUxzteVn440wHmbETHthJ7va7f+2iHR5Rr3OjE5LBZ704lp3dtgKegZd+iTg9bkXdirajIdhal+xdYUiYUmeCEHxwgbGWIsK8rsYTkbaYueXr7XtqYuc1Vfz/7Gj0GQsMMVgcwHP3gwMJMImV7GeATmk=\n-----END RSA PRIVATE KEY-----'#完整公钥内容 sa.pub
public_key = b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwojpJBO8QZla+3sfdYji4aXXPesNAI9ScNOpyP4mcFLNxDOQluf7TBIQfzGC7Ynk58TXv9KeC4jO8FK2UsyjF9bKf8O3j3EhO+t8zNUD35vsqovTFyr1TvvjA8MMaH51lRIvKIQma58li4yh844lauwR4O7zAnd0RIg7ib7WbwCtkMw7RPdliy84CLsHdxSh4TZ1uWkiVb8eF5WT/28PnL5VwVHhg3A2v+JVBt4Fd9aNjanMBxIq5Tt0hlGOEmc7rCUmf/WFQz4Ddw54ZYQ+cD7Y9HwTqzCWMW14YbYNmohzn/fNpX5vXevZhX8aFjcgTYW5PX8jXAbBOcjWLtsXSwIDAQAB\n-----END PUBLIC KEY-----'#payload信息
payload={"iss":"kubernetes/serviceaccount","kubernetes.io/serviceaccount/namespace":"test","kubernetes.io/serviceaccount/secret.name":"sa1-token-p5wxt","kubernetes.io/serviceaccount/service-account.name":"sa1","kubernetes.io/serviceaccount/service-account.uid":"2a457ffc-53bb-4a67-bd38-9e7fb048758e","sub":"system:serviceaccount:test:sa1"}#签名
encoded=jwt.encode(payload, private_key, algorithm='RS256', headers={"alg":"RS256","kid":"M11kEZjXxDvNYQ44jM0l4D4lX7gz6JxeEtvgtMY-Rjc"})
print("encode value:")
print(encoded)#验证
decoded = jwt.decode(encoded, public_key, algorithms='RS256')
print("\ndecode value:")
print(decoded)

使用python3执行脚本(为什么使用python3?原因在下面),查看结果

root@master:~/token# /usr/bin/python3 ./test.py
encode value:
b'eyJhbGciOiJSUzI1NiIsImtpZCI6Ik0xMWtFWmpYeER2TllRNDRqTTBsNEQ0bFg3Z3o2SnhlRXR2Z3RNWS1SamMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJ0ZXN0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhMS10b2tlbi1wNXd4dCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJzYTEiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyYTQ1N2ZmYy01M2JiLTRhNjctYmQzOC05ZTdmYjA0ODc1OGUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6dGVzdDpzYTEifQ.dd2L5lZHhdSWYai7u3W9vuamc2q4HW65U5q4EtqhAl65yrDIoDRQz9T9gwQVLUd91j4wNj_WGo6s0CZ9bbGydnTM2HF4c3zJBZdZdALbYA7SEs8N7Z70dA_OW_N1AVGTWXHV7kY5E8rYZopfXV1fiIL_nrzy0YxEfOylz5YYuACd4duUuZ_cJuzE0WcLHLHuXWptg2AnF0jnYK5XVeESDt8hL9PG8QOu3W3T2V0sMo9e9PW38PxNuSbF35rUTX13aYDoWhxIBYyCJIEFiNnsfRGNufgdw2VBpGz3RT3TUQI4c7ZW0t29JUYxdQ9mziwIHf9RVAWypeeG3Zk4zNw5RQ'#可看到decode后的值就是payload的内容
decode value:
{'iss': 'kubernetes/serviceaccount', 'kubernetes.io/serviceaccount/namespace': 'test', 'kubernetes.io/serviceaccount/secret.name': 'sa1-token-p5wxt', 'kubernetes.io/serviceaccount/service-account.name': 'sa1', 'kubernetes.io/serviceaccount/service-account.uid': '2a457ffc-53bb-4a67-bd38-9e7fb048758e', 'sub': 'system:serviceaccount:test:sa1'}

使用python库时有两点需要注意

a. python2中字典是无序的,即输出字典内容时不按照输入顺序。而payload内容是以字典形式作为函数jwt.encode输入的,但是在函数jwt.encode中,payload字典顺序已经变了,而计算hash值时,顺序不同生成的hash值也不同,python3保证了字典顺序,所以使用python3执行脚本。
b. header中指定了 alg 和 kid,但是python jwt库会自动添加上 type=JWT,这样的话就变成了三个字段,而使用jwt.io图形界面生成JWT时,填几个字段就用几个字段生成JWT,
结果导致同样的输入参数,python库和jwt.io生成的结果不一致。可以修改python jwt库,将 type=JWT 去掉,如下代码

/usr/lib/python3/dist-packages/jwt/api_jws.py76     def encode(self,77                payload,  # type: Union[Dict, bytes]78                key,  # type: str79                algorithm='HS256',  # type: str80                headers=None,  # type: Optional[Dict]81                json_encoder=None  # type: Optional[Callable]82                ):83         segments = []8485         if algorithm is None:86             algorithm = 'none'8788         if algorithm not in self._valid_algs:89             pass9091         # Header92         header = {'typ': self.header_typ, 'alg': algorithm}9394         if headers:95             self._validate_headers(headers)//会自动将headers更新到header中,如果下面使用header的话就会多一个字段96             header.update(headers)9798         json_header = force_bytes(99             json.dumps(
100                 headers,  //这里原先是使用header,替换成headers即可
101                 separators=(',', ':'),
102                 cls=json_encoder
103             )
104         )

下面内容是python调试内容,可忽略。

(Pdb) p signature
b"u\xdd\x8b\xe6VG\x85\xd4\x96a\xa8\xbb\xbbu\xbd\xbe\xe6\xa6sj\xb8\x1dn\xb9S\x9a\xb8\x12\xda\xa1\x02^\xb9\xca\xb0\xc8\xa04P\xcf\xd4\xfd\x83\x04\x15-G}\xd6>06?\xd6\x1a\x8e\xac\xd0&}m\xb1\xb2vt\xcc\xd8qxs|\xc9\x05\x97Yt\x02\xdb`\x0e\xd2\x12\xcf\r\xed\x9e\xf4t\x0f\xce[\xf3u\x01Q\x93Yq\xd5\xeeF9\x13\xca\xd8f\x8a_]]_\x88\x82\xff\x9e\xbc\xf2\xd1\x8cD|\xec\xa5\xcf\x96\x18\xb8\x00\x9d\xe1\xdb\x94\xb9\x9f\xdc&\xec\xc4\xd1g\x0b\x1c\xb1\xee]jm\x83`'\x17H\xe7`\xaeWU\xe1\x12\x0e\xdf!/\xd3\xc6\xf1\x03\xae\xddm\xd3\xd9],2\x8f^\xf4\xf5\xb7\xf0\xfcM\xb9&\xc5\xdf\x9a\xd4M}wi\x80\xe8Z\x1cH\x05\x8c\x82$\x81\x05\x88\xd9\xec}\x11\x8d\xb9\xf8\x1d\xc3eA\xa4l\xf7E=\xd3Q\x028s\xb6V\xd2\xdd\xbd%F1u\x0ff\xce,\x08\x1d\xffQT\x05\xb2\xa5\xe7\x86\xdd\x998\xcc\xdc9E"
(Pdb) print(signature.hex())
75dd8be6564785d49661a8bbbb75bdbee6a6736ab81d6eb9539ab812daa1025eb9cab0c8a03450cfd4fd8304152d477dd63e30363fd61a8eacd0267d6db1b27674ccd87178737cc90597597402db600ed212cf0ded9ef4740fce5bf3750151935971d5ee463913cad8668a5f5d5d5f8882ff9ebcf2d18c447ceca5cf9618b8009de1db94b99fdc26ecc4d1670b1cb1ee5d6a6d8360271748e760ae5755e1120edf212fd3c6f103aedd6dd3d95d2c328f5ef4f5b7f0fc4db926c5df9ad44d7d776980e85a1c48058c8224810588d9ec7d118db9f81dc36541a46cf7453dd351023873b656d2ddbd254631750f66ce2c081dff515405b2a5e786dd9938ccdc3945
(Pdb) n
> /usr/lib/python3/dist-packages/jwt/api_jws.py(127)encode()
-> return b'.'.join(segments)
(Pdb) segments
[b'eyJhbGciOiJSUzI1NiIsImtpZCI6Ik0xMWtFWmpYeER2TllRNDRqTTBsNEQ0bFg3Z3o2SnhlRXR2Z3RNWS1SamMifQ', b'eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJ0ZXN0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhMS10b2tlbi1wNXd4dCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJzYTEiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIyYTQ1N2ZmYy01M2JiLTRhNjctYmQzOC05ZTdmYjA0ODc1OGUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6dGVzdDpzYTEifQ', b'dd2L5lZHhdSWYai7u3W9vuamc2q4HW65U5q4EtqhAl65yrDIoDRQz9T9gwQVLUd91j4wNj_WGo6s0CZ9bbGydnTM2HF4c3zJBZdZdALbYA7SEs8N7Z70dA_OW_N1AVGTWXHV7kY5E8rYZopfXV1fiIL_nrzy0YxEfOylz5YYuACd4duUuZ_cJuzE0WcLHLHuXWptg2AnF0jnYK5XVeESDt8hL9PG8QOu3W3T2V0sMo9e9PW38PxNuSbF35rUTX13aYDoWhxIBYyCJIEFiNnsfRGNufgdw2VBpGz3RT3TUQI4c7ZW0t29JUYxdQ9mziwIHf9RVAWypeeG3Zk4zNw5RQ']

sha256生成的hash值
(Pdb) p data
b'\x9e,\x86\xd2g74\xed\xdbo\x14\xaa\x02b\xd6\x01a\xa6\xde\xfb\x82\xd0,\xe0\x14\xde\x82\xbe\xde\xed\x97'
(Pdb) len(data)
32
(Pdb) print(data.hex())
9e2a2c86d2673734eddb6f14aa0262d60161a6defb82d02ce014de82bedeed97
(Pdb) print(data)
b'\x9e
,\x86\xd2g74\xed\xdbo\x14\xaa\x02b\xd6\x01a\xa6\xde\xfb\x82\xd0,\xe0\x14\xde\x82\xbe\xde\xed\x97'

/usr/lib/python3/dist-packages/jwt/api_jws.py
alg_obj = self._algorithms[algorithm]
key = alg_obj.prepare_key(key)
signature = alg_obj.sign(signing_input, key)

/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/rsa.py(414)sign()
def sign(self, data, padding, algorithm):
data, algorithm = _calculate_digest_and_algorithm(
self._backend, data, algorithm
)
return _rsa_sig_sign(self._backend, padding, algorithm, self, data)

也可参考:k8s之 service account token - 简书 (jianshu.com)

k8s之 service account token相关推荐

  1. 在Kubernetes Pod中使用Service Account访问API Server

    2019独角兽企业重金招聘Python工程师标准>>> 在Kubernetes Pod中使用Service Account访问API Server 博客分类: Kubernetes ...

  2. kubernetes的Service Account和secret关系

    https://blog.csdn.net/ccy19910925/article/details/80610336 Service Account Service Account概念的引入是基于这样 ...

  3. k8s的认证和service account简述

    k8s的认证: 与API server通信的客户端大致有两类:   1.集群客户端工具(kubectl.kubeadm.kubelet等)   2.集群内pod. 任何客户端访问k8s时的过程:   ...

  4. 解决谷歌翻译TranslateException: Error getting access token for service account: Connection reset

    最近一直在调试谷歌翻译的api,根据前两篇的相关文章,在本地的windows系统和Linux系统都配置好了环境变量和代理.剩下的就是运行代码实现翻译.windows下的很顺利直接拿谷歌提供的demo就 ...

  5. kubernetes的Service Account

    kubernetes的Service Account Service account作用 Service account是为了方便Pod里面的进程调用Kubernetes API或其他外部服务. Se ...

  6. [置顶]kubernetes资源类型--secret和Service Account

    secret 概念 secret对象类型主要目的是保存和处理敏感信息/私密数据,比如密码,OAuth tokens,ssh keys等信息.将这些信息放在secret对象中比 直接放在pod或dock ...

  7. kubernetes资源类型--secret和Service Account

    secret 概念 secret对象类型主要目的是保存和处理敏感信息/私密数据,比如密码,OAuth tokens,ssh keys等信息.将这些信息放在secret对象中比 直接放在pod或dock ...

  8. kubernetes的Service Account和secret

    Service Account Service Account概念的引入是基于这样的使用场景:运行在pod里的进程需要调用Kubernetes API以及非Kubernetes API的其它服务.Se ...

  9. 服务账户service account在kubernetes1.24中的变化

    当创建pod时需要在pod里的.spec.serviceAccount指定pod以哪个service account运行,如果没有指定的话则默认使用default这个sa. 然后通过投射卷,在pod的 ...

最新文章

  1. Kafka团队修改KSQL开源许可,怒怼云厂商
  2. 北京活动 | 新书首发手把手带你的产品从0开始验证一个新的业务
  3. The Preliminary Contest for ICPC China Nanchang National Invitational
  4. [BZOJ1385][Baltic2000]Division expression
  5. java的linux执行的shell
  6. python深拷贝实现原理,js递归实现深拷贝
  7. 怎么查到运行的时间_“我的成考录取通知书怎么还没来,它是不是迷路了?”...
  8. Ora-00600 错误的代码含义及常用查询
  9. 【AI手机三国杀】三星Bixby闯入战场,AI功能集成颠覆人机交互
  10. 牛b硬件信息修改大师_比X大师更靠谱?一款真正良心的硬件检测工具
  11. go-sqlite3 “database is locked”问题解决方案
  12. 安川ga700变频器故障码集_安川变频器故障代码
  13. 第四节 二维连续型随机变量及分布、条件概率密度
  14. qt android刘海屏状态栏,安卓手机刘海屏算抄袭苹果iPhone X吗?真相了
  15. 梯度下降法算法比较和进一步优化
  16. 成绩不高?校园招聘了解一下!春招公司大全!
  17. 高德地图手机端定位偏移问题
  18. 怎么学习嵌入式技术开发
  19. 宏观经济需求与供给分析之:需求的收入效应、替代效应和需求定律
  20. 微信小程序项目从app.js中获取数据

热门文章

  1. 太美医疗ctms和etmf可以解决什么问题
  2. 考研计算机统考知识点,09计算机考研统考大纲权威解读之考试范围
  3. 与游戏世界交互——Hit UFO
  4. 算法实习生学习之路--UCB算法
  5. python 跨平台打包_用python打包exe应用程序-PyInstaller
  6. 关于qt上实现基于百度的语音识别
  7. Java阉割版记账系统
  8. python内置装饰器property_Python中的内置装饰器之property
  9. js中的深浅复制(深浅拷贝、深浅克隆)
  10. 小米官网重做jQuery