全網(wǎng)最簡單的k8s User JWT token管理器

> [kubernetes集群三步安裝](https://sealyun.com/pro/products/)

# 概述

kubernetes server account的token很容易獲取素跺,但是User的token非常麻煩河狐,本文給出一個極簡的User token生成方式到推,讓用戶可以一個http請求就能獲取到。

## token主要用來干啥

官方dashboard登錄時需要袍辞。 如果通過使用kubeconfig文件登錄而文件中又沒有token的話會失敗,現(xiàn)在大部分文章都介紹使用service account的token來登錄dashboard,能通,不過有問題:

第一:綁定角色時要指定類型是service account:

```

apiVersion: rbac.authorization.k8s.io/v1beta1

kind: ClusterRoleBinding

metadata:

? name: kubernetes-dashboard

? labels:

? ? k8s-app: kubernetes-dashboard

roleRef:

? apiGroup: rbac.authorization.k8s.io

? kind: ClusterRole

? name: cluster-admin

subjects:

- kind: ServiceAccount? # 這里不是User類型

? name: kubernetes-dashboard

? namespace: kube-system

```

第二:要理解kubeconfig里是解析證書把CN作為用戶名的铛碑,這時service account即便與CN一樣那還是兩個賬戶,綁定角色時還需要綁定兩次虽界,有點像把service account給"人"用, 所以把service account的token扔給某個開發(fā)人員去用往往不合適,service account token更多時候是給程序用的涛菠。

想直接調(diào)用https的莉御,沒有token就會:

```

[root@iZj6cegflzze2l7fpcqoerZ ssl]# curl https://172.31.12.61:6443/api/v1/namespaces/default/pods --insecure

{

? "kind": "Status",

? "apiVersion": "v1",

? "metadata": {

? },

? "status": "Failure",

? "message": "pods is forbidden: User \"system:anonymous\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",

? "reason": "Forbidden",

? "details": {

? ? "kind": "pods"

? },

? "code": 403

}

```

? 因為沒有任何認證信息,所以匿名(anonymous)用戶沒有任何權(quán)限

加了token是這樣的:

```

[root@iZj6cegflzze2l7fpcqoerZ ssl]# curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkNnYzRPVEV5TlRVM0VnWm5hWFJvZFdJIn0.eyJpc3MiOiJodHRwczovL2RleC5leGFtcGxlLmNvbTo4MDgwIiwic3ViIjoiQ2djNE9URXlOVFUzRWdabmFYUm9kV0kiLCJhdWQiOiJleGFtcGxlLWFwcCIsImV4cCI6MTU1MTA5NzkwNiwiaWF0IjoxNTUwNzM3OTA2LCJlbWFpbCI6ImZodGpvYkBob3RtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJncm91cHMiOlsiZGV2Il0sIm5hbWUiOiJmYW51eCJ9.ZqKn461UW0aGtyjyqu2Dc5tiUzC-6eYLag542d3AvklUdZuw8i9XwyaUg_f1OAj0ZsEcOybOe9_PeGMaUYzU0OvlKPY-q2zbQVC-m6u6sQw6ZXx8pi0W8k4wQSJnMaOLddCfurlYufmr8kScDBQlnKapSR0F9mJzvpKkHD-XNshQKWhX3n03g7OfFgb4RuhLjKDNQnoGn7DfBNntibHlF9sPo0jC5JjqTZaGvoGmiRE4PAXwxA-RJifsWDNf_jW8lrDiY4NSO_3O081cia4N1GKht51q9W3eaNMvFDD9hje7abDdZoz9KPi2vc3zvgH7cNv0ExVHKaA0-dwAZgTx4g" -k https://172.31.12.61:6443/api/v1/namespaces/default/pods

{

? "kind": "Status",

? "apiVersion": "v1",

? "metadata": {

? },

? "status": "Failure",

? "message": "pods is forbidden: User \"https://dex.example.com:8080#fanux\" cannot list resource \"pods\" in API group \"\" in the namespace \"default\"",

? "reason": "Forbidden",

? "details": {

? ? "kind": "pods"

? },

? "code": 403

}

```

? 看俗冻,雖然還是403 但是已經(jīng)有了用戶信息礁叔,只要給該用戶授權(quán)就可正常訪問了,如何授權(quán)下文介紹

## token種類介紹

token的生成方式有很多迄薄,主要分成三種:

1. service account token 這個創(chuàng)建service account就有琅关,存在secret里 獲取比較簡單,但是要區(qū)分好 [User 和 service account區(qū)別](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#users-in-kubernetes)

2. 普通的token讥蔽,這種token就是個普通的字符串涣易,一般是自己寫一個認證的web hook, k8s認證時調(diào)用這個hook 查詢token是否有效,比較low

3. 基于openid的jwt(josn web token) 這種token冶伞,認證中心把用戶信息放在json里新症,用私鑰加密,k8s拿到token后用公鑰解密响禽,只要解密成功token就是合法的而且能拿到用戶信息徒爹,不需要再像認證中心請求

基于openid的jwt是本文介紹的重點。

社區(qū)用的比較多的就是[dex](https://github.com/coreos/dex),是一個比較完整的實現(xiàn)芋类,但是對于不熟悉該技術(shù)的朋友來說還是有點門檻的隆嗅,容易繞進去。 而且還存在一些使用不方便的問題侯繁。

如依賴復雜胖喳,首先得需要一個真正的用戶管理程序,如ldap 或者一個auth2服務端巫击,這還可以接受禀晓,關(guān)鍵是認證時可能需要依賴瀏覽器進行跳轉(zhuǎn)授權(quán)精续,這在十分多的場景里就變的十分尷尬,就比如我們的場景壓根沒有

界面粹懒,這樣生成token就成了一個大問題重付。? 其次集成到別的系統(tǒng)中時往往用戶已經(jīng)登錄過了,所以需要一個二次授權(quán)的過程才能拿到token凫乖,依賴過重導致系統(tǒng)難以設(shè)計确垫。

然而如果不是集成到別的系統(tǒng)中,比如從0開發(fā)一個完成的PaaS平臺那使用dex還是一個完美的方案帽芽。

所以我們實現(xiàn)了一個簡單粗暴的方案删掀,完全解放了這個過程, 只care最核心的東西。

# sealyun fist介紹

我們想要啥?

input:

```

{

? ? "User": "fanux",

? ? "Group": ["sealyun", "develop"]

}

```

output:

```

eyJhbGciOiJSUzI1NiIsImtpZCI6IkNnYzRPVEV5TlRVM0VnWm5hWFJvZFdJIn0.eyJpc3MiOiJodHRwczovL2RleC5leGFtcGxlLmNvbTo4MDgwIiwic3ViIjoiQ2djNE9URXlOVFUzRWdabmFYUm9kV0kiLCJhdWQiOiJleGFtcGxlLWFwcCIsImV4cCI6MTU1MTA5NzkwNiwiaWF0IjoxNTUwNzM3OTA2LCJlbWFpbCI6ImZodGpvYkBob3RtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJncm91cHMiOlsiZGV2Il0sIm5hbWUiOiJmYW51eCJ9.ZqKn461UW0aGtyjyqu2Dc5tiUzC-6eYLag542d3AvklUdZuw8i9XwyaUg_f1OAj0ZsEcOybOe9_PeGMaUYzU0OvlKPY-q2zbQVC-m6u6sQw6ZXx8pi0W8k4wQSJnMaOLddCfurlYufmr8kScDBQlnKapSR0F9mJzvpKkHD-XNshQKWhX3n03g7OfFgb4RuhLjKDNQnoGn7DfBNntibHlF9sPo0jC5JjqTZaGvoGmiRE4PAXwxA-RJifsWDNf_jW8lrDiY4NSO_3O081cia4N1GKht51q9W3eaNMvFDD9hje7abDdZoz9KPi2vc3zvgH7cNv0ExVHKaA0-dwAZgTx4g

```

結(jié)束,多簡單导街,別整那么多沒用的披泪。

所以為了實現(xiàn)上面的功能,我們開發(fā)了 [fist](https://github.com/fanux/fist), fist的auth模塊把dex里最核心的token生成功能以及jwt功能實現(xiàn)了搬瑰。

# sealyun fist/auth 使用教程

## 安裝部署

> 生成證書

```

# mkdir /etc/kubernetes/pki/fist

# cd /etc/kubernetes/pki/fist

# sh gencert.sh # 腳本內(nèi)容內(nèi)代碼

```

> 啟動fist auth模塊

```

kubectl create -f deploy/fist-auth.yaml

```

> 修改k8s apiserver啟動參數(shù)

```

vim /etc/kubernetes/manifests/kube-apiserver.yaml

```

```

? - command:

? ? - kube-apiserver

? ? - --oidc-issuer-url=https://fist.sealyun.svc.cluster.local:8080

? ? - --oidc-client-id=example-app

? ? - --oidc-ca-file=/etc/kubernetes/pki/fist/ca.pem

? ? - --oidc-username-claim=name

? ? - --oidc-groups-claim=groups

```

## 獲取及使用 token

> 獲取token

```

curl https://fist.sealyun.svc.cluster.local:8080/token?user=fanux&group=sealyun,develop --cacert ca.pem

```

> 使用token

直接curl加bare token 見上文

加入到kubeconfig中:

```

kubectl config set-credentials --token=eyJhbGciOiJSUzI1NiIsImtpZCI6IkNnYzRPVEV5TlRVM0VnWm5hWFJvZFdJIn0.eyJpc3MiOiJodHRwczovL2RleC5leGFtcGxlLmNvbTo4MDgwIiwic3ViIjoiQ2djNE9URXlOVFUzRWdabmFYUm9kV0kiLCJhdWQiOiJleGFtcGxlLWFwcCIsImV4cCI6MTU1MTEwMDI5MywiaWF0IjoxNTUwNzQwMjkzLCJlbWFpbCI6ImZodGpvYkBob3RtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJncm91cHMiOlsiZGV2Il0sIm5hbWUiOiJmYW51eCJ9.OAK4oIYqJszm1EACYW2neXTo738RW9kXFOIN5bOT4Z2CeKAvYqyOVKCWZf04xX45jwT78mATR3uas2YvRooDXlvxaD3K43ls4KBSG-Ofp-ynqlcVTpD3sUDqyux2iieNv4N6IyCv11smrU0lIlkrQC6oyxzTGae1FrJVGc5rHNsIRZHp2WrQvw83uLn_elHgUfSlsOq0cPtVONaAQWMAMi2DX-y5GCNpn1CDvudGJihqsTciPx7bj0AOXyiOznWhV186Ybk-Rgqn8h0eBaQhFMyNpwVt6oIP5pvJQs0uoODeRv6P3I3-AjKyuCllh9KDtlCVvSP4WtMUTfHQN4BigQ? kubernetes-admin

```

然后.kube/config 文件里的 user.client-certifacate-data 和 client-key-data就可以刪了款票,再執(zhí)行kubectl會:

```

[root@iZj6cegflzze2l7fpcqoerZ ~]# kubectl get pod

Error from server (Forbidden): pods is forbidden: User "https://dex.example.com:8080#fanux" cannot list resource "pods" in API group "" in the namespace "default"

```

說明新用戶成功了

> 授權(quán)

```

[root@iZj6cegflzze2l7fpcqoerZ ~]# cat rolebind.yaml

kind: ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

? name: read-secrets-global

subjects:

- kind: User

? name: "https://dex.example.com:8080#fanux" # Name is case sensitive

? apiGroup: rbac.authorization.k8s.io

roleRef:

? kind: ClusterRole

? name: cluster-admin? # 超級用戶給他

? apiGroup: rbac.authorization.k8s.io

```

創(chuàng)建個role binding即可:

```

[root@iZj6cegflzze2l7fpcqoerZ ~]# kubectl? --kubeconfig /etc/kubernetes/admin.conf create? -f rolebind.yaml # 用管理員的kubeconfig

clusterrolebinding.rbac.authorization.k8s.io/read-secrets-global created

[root@iZj6cegflzze2l7fpcqoerZ ~]# kubectl get pod # 有權(quán)限訪問pod了

No resources found.

```

# 原理介紹

## jwt原理

```

? ? ? ? ? ? ? ? ? ? ? https://fist.sealyun.cluster.local:8080

k8s? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? jwt server

|? /.well-known/openid-configuration? ? ? ? ? ? |

|------------------------------------------------>|? k8s通過此url發(fā)現(xiàn)一些信息,最重要的就是用于校驗token公鑰的地址

|? discover info? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |

|<------------------------------------------------|

|? ? /keys? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |

|------------------------------------------------>|? 上一步拿到地址泽论,這一步獲取到公鑰

|? ? public keys? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |

|<------------------------------------------------|

|? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |

```

discoer info 是個json:

```

{

"issuer": "https://accounts.google.com",

"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",

"token_endpoint": "https://oauth2.googleapis.com/token",

"userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",

"revocation_endpoint": "https://oauth2.googleapis.com/revoke",

"jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",

"response_types_supported": [

"code",

"token",

"id_token",

"code token",

"code id_token",

"token id_token",

"code token id_token",

"none"

],

...

```

public keys也是個json 類似:

```

{

"keys": [

{

"e": "AQAB",

"kty": "RSA",

"alg": "RS256",

"n": "3MdFK4pXPvehMipDL_COfqn6o9soHgSaq_V1o8U_5gTZ-j9DxO9PV7BVncXBgHFctnp3JQ1QTDF7txeHeuLOS4KziRw5r4ohaj2WoOTqXh7lqVMR2YDAcBK46asS177NpkQ1CqHIsy3kNfqhXLwTaKfdlwdA_XUfRbKORWbq0kDxV35egx35nHl5qJ6aP6fcpsnnPvHf7KWO0zkdvwuR-IX79HjqUAEg5UERd5FK4y06PRbxuXHjAgVhHu_sk4reNXNp1HRuTYtQ26DFbVaIjsWb8-nQC8-7FkTjlw9FteAwLVGOm9sTLFp73jAf0pWLh7sJ02pBxZKjsxLO1Lvg7w",

"use": "sig",

"kid": "7c309e3a1c1999cb0404ab7125ee40b7cdbcaf7d"

},

{

"alg": "RS256",

"n": "2K7epoJWl_B68lRUi1txaa0kEuIK4WHiHpi1yC4kPyu48d046yLlrwuvbQMbog2YTOZdVoG1D4zlWKHuVY00O80U1ocFmBl3fKVrUMakvHru0C0mAcEUQo7ItyEX7rpOVYtxlrVk6G8PY4EK61EB-Xe35P0zb2AMZn7Tvm9-tLcccqYlrYBO4SWOwd5uBSqc_WcNJXgnQ-9sYEZ0JUMhKZelEMrpX72hslmduiz-LMsXCnbS7jDGcUuSjHXVLM9tb1SQynx5Xz9xyGeN4rQLnFIKvgwpiqnvLpbMo6grhJwrz67d1X6MwpKtAcqZ2V2v4rQsjbblNH7GzF8ZsfOaqw",

"use": "sig",

"kid": "7d680d8c70d44e947133cbd499ebc1a61c3d5abc",

"e": "AQAB",

"kty": "RSA"

}

]

}

```

所以fist只需要實現(xiàn)這兩個url 和 用私鑰匙加密用戶信息生成token即可艾少。

創(chuàng)建密鑰對:

```

key, err := rsa.GenerateKey(rand.Reader, 2048)

if err != nil {

log.Fatalf("gen rsa key: %v", err)

}

priv = jose.JSONWebKey{

Key:? ? ? key,

KeyID:? ? "Cgc4OTEyNTU3EgZnaXRodWI",

Algorithm: "RS256",

Use:? ? ? "sig",

}

pub = jose.JSONWebKey{

Key:? ? ? key.Public(),

KeyID:? ? "Cgc4OTEyNTU3EgZnaXRodWI",

Algorithm: "RS256",

Use:? ? ? "sig",

}

```

私鑰加密:

```

tok := idTokenClaims{

Issuer:? ? ? ? "https://dex.example.com:8080",

Subject:? ? ? "Cgc4OTEyNTU3EgZnaXRodWI",

Audience:? ? ? "example-app",

Expiry:? ? ? ? time.Now().Add(time.Hour * 100).Unix(),

IssuedAt:? ? ? time.Now().Unix(),

Email:? ? ? ? "fhtjob@hotmail.com",

EmailVerified: &ev,

Groups:? ? ? ? []string{"dev"},

Name:? ? ? ? ? "fanux",

}

payload, err := json.Marshal(&tok)

if err != nil {

return

}

var idToken string

if idToken, err = signPayload(&Priv, signingAlg, payload); err != nil {

return

```

# 總結(jié)

fist核心代碼已經(jīng)可用,不過為了更方便使用還需要進一步梳理翼悴,敬請期待缚够。 鑒權(quán)僅是其其中一個功能,fist定位是一個極簡的k8s管理平臺鹦赎。

探討可加QQ群:98488045

> 公眾號:

![sealyun](https://sealyun.com/kubernetes-qrcode.jpg)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谍椅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子钙姊,更是在濱河造成了極大的恐慌毯辅,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件煞额,死亡現(xiàn)場離奇詭異思恐,居然都是意外死亡,警方通過查閱死者的電腦和手機膊毁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門胀莹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人婚温,你說我怎么就攤上這事描焰。” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵荆秦,是天一觀的道長篱竭。 經(jīng)常有香客問我,道長步绸,這世上最難降的妖魔是什么掺逼? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮瓤介,結(jié)果婚禮上吕喘,老公的妹妹穿的比我還像新娘。我一直安慰自己刑桑,他們只是感情好氯质,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著祠斧,像睡著了一般闻察。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上琢锋,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天蜓陌,我揣著相機與錄音,去河邊找鬼吩蔑。 笑死,一個胖子當著我的面吹牛填抬,可吹牛的內(nèi)容都是我干的烛芬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼飒责,長吁一口氣:“原來是場噩夢啊……” “哼赘娄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宏蛉,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤遣臼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拾并,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揍堰,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年嗅义,在試婚紗的時候發(fā)現(xiàn)自己被綠了屏歹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡之碗,死狀恐怖蝙眶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情褪那,我是刑警寧澤幽纷,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布式塌,位于F島的核電站,受9級特大地震影響友浸,放射性物質(zhì)發(fā)生泄漏峰尝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一尾菇、第九天 我趴在偏房一處隱蔽的房頂上張望境析。 院中可真熱鬧,春花似錦派诬、人聲如沸劳淆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沛鸵。三九已至,卻和暖如春缆八,著一層夾襖步出監(jiān)牢的瞬間曲掰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工奈辰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留栏妖,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓奖恰,卻偏偏與公主長得像吊趾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瑟啃,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容