API Server權限控制分爲三種:Authentication(身份認證)、Authorization(受權)、AdmissionControl(准入控制)。linux
當客戶端向Kubernetes非只讀端口發起API請求時,Kubernetes經過三種方式來認證用戶的合法性。kubernetes中,驗證用戶是否有權限操做api的方式有三種:證書認證,token認證,基本信息認證。redis
證書認證json
設置apiserver的啓動參數:--client_ca_file=SOMEFILE ,這個被引用的文件中包含的驗證client的證書,若是被驗證經過,那麼這個驗證記錄中的主體對象將會做爲請求的username。api
Token認證數組
設置apiserver的啓動參數:--token_auth_file=SOMEFILE。 token file的格式包含三列:token,username,userid。當使用token做爲驗證方式時,在對apiserver的http請求中,增長 一個Header字段:Authorization ,將它的值設置爲:Bearer SOMETOKEN。瀏覽器
基本信息認證安全
設置apiserver的啓動參數:--basic_auth_file=SOMEFILE,若是更改了文件中的密碼,只有從新啓動apiserver使 其從新生效。其文件的基本格式包含三列:passwork,username,userid。當使用此做爲認證方式時,在對apiserver的http 請求中,增長一個Header字段:Authorization,將它的值設置爲: Basic BASE64ENCODEDUSER:PASSWORD。數據結構
在Kubernetes中,認證和受權是分開的,並且受權發生在認證完成以後,認證過程是檢驗發起API請求的用戶是否是他所聲稱的那我的。而受權過程則 判斷此用戶是否有執行該API請求的權限,所以受權是以認證的結果做爲基礎的。Kubernetes受權模塊應用於全部對APIServer的HTTP訪 問請求(只讀端口除外),訪問只讀端口不須要認證和受權過程。APIServer啓動時默認將authorization_mode設置爲 AlwaysAllow模式,即永遠容許。app
Kubernetes受權模塊檢查每一個HTTP請求並提取請求上下文中的所需屬性(例如:user,resource kind,namespace)與訪問控制規則進行比較。任何一個API請求在被處理前都須要經過一個或多個訪問控制規則的驗證。curl
目前Kubernetes支持並實現瞭如下的受權模式(authorization_mode),這些受權模式能夠經過在apiserver啓動時傳入參數進行選擇。
--authorization_mode=AlwaysDeny
--authorization_mode=AlwaysAllow
--authorization_mode=ABAC
AlwaysDeny 模式屏蔽全部的請求(通常用於測試)。AlwaysAllow模式容許全部請求,默認apiserver啓動時採用的即是AlwaysAllow模式)。 ABAC(Attribute-Based Access Control,即基於屬性的訪問控制)模式則容許用戶自定義受權訪問控制規則。
ABAC模式:
一個API請求中有4個屬性被用於用戶受權過程:
UserName:String類型,用於標識發起請求的用戶。若是不進行認證、受權操做,則該字符串爲空。
ReadOnly:bool類型,標識該請求是否僅進行只讀操做(GET就是隻讀操做)。
Kind:String類型,用於標識要訪問的Kubernetes資源對象的類型。當訪問例如/api/v1beta1/pods等API endpoint時,Kind屬性才非空,但訪問其餘endpoint時,例如/version,/healthz等,Kind屬性爲空。
Namespace:String類型,用於標識要訪問的Kubernetes資源對象所在的namespace。
對ABAC模式,在apiserver啓動時除了須要傳入--authorization_mode=ABAC選項外,還須要指定 --authorization_policy_file=SOME_FILENAME。authorization_policy_file文件的每一 行都是一個JSON對象,該JSON對象是一個沒有嵌套的map數據結構,表明一個訪問控制規則對象。一個訪問控制規則對象是一個有如下字段的map:
user:--token_auth_file指定的user字符串。
readonly:true或false,若是是true則代表該規則只應用於GET請求。
kind:Kubernetes內置資源對象類型,例如pods、events等。
namespace:也能夠縮寫成ns。
一個簡單的訪問控制規則文件以下所示,每一行定義一條規則。
{"user":"admin"}
{"user":"alice", "ns": "projectCaribou"}
{"user":"kubelet", "readonly": true, "kind": "pods"}
{"user":"kubelet", "kind": "events"}
{"user":"bob", "kind": "pods", "readonly": true, "ns": "projectCaribou"}
注:缺省的字段與該字段類型的零值(空字符串,0,false等)等價。
規則逐行說明以下。
第一行代表,admin能夠作任何事情,不受namespace,資源類型,請求類型的限制。
第二行代表,alice可以在namespace "projectCaribou"中作任何事情,不受資源類型,請求類型的限制。
第三行代表,kubelet有權限讀任何一個pod的信息。
第四行代表,kubelet有權限讀寫任何一個event。
第五行代表,Bob有權限讀取在namespace "projectCaribou"中全部pod的信息。
一個受權過程就是一個比較API請求中各屬性與訪問控制規則文件中對應的各字段是否匹配的一個過程。當apiserver接收到一個API請求時,該請求 的各屬性就已經肯定了,若是有一個屬性未被設置,則apiserver將其設爲該類型的空值(空字符串,0,false等)。匹配規則很簡單,以下所示。
若是API請求中的某個屬性爲空值,則規定該屬性與訪問控制規則文件中對應的字段匹配。
若是訪問控制規則的某個字段爲空值,則規定該字段與API請求的對應屬性匹配。
若是API請求中的屬性值非空且訪問控制規則的某個字段值也非空,則將這兩個值進行比較,若是相同則匹配,反之則不匹配。
API請求的屬性元組(tuple)會與訪問控制規則文件中的全部規則逐條匹配,只要有一條匹配則表示匹配成功,如若否則,則受權失敗。
准入控制admission controller本質上爲一段准入代碼,在對kubernetes api的請求過程當中,順序爲 先通過 認證 & 受權,而後執行准入操做,再對目標對象進行操做。這個准入代碼在apiserver中,並且必須被編譯到二進制文件中才能被執行。
在對集羣進行請求時,每一個准入控制代碼都按照必定順序執行。若是有一個准入控制拒絕了這次請求,那麼整個請求的結果將會當即返回,並提示用戶相應的error信息。
在某些狀況下,爲了適用於應用系統的配置,准入邏輯可能會改變目標對象。此外,准入邏輯也會改變請求操做的一部分相關資源。
做用
在kubernetes中,一些高級特性正常運行的前提條件爲,將一些准入模塊處於enable狀態。總結下,對於kubernetes apiserver,若是不適當的配置准入控制模塊,它就不能稱做是一個完整的server,某些功能也不會正常的生效。
開啓方式
在kubernetes apiserver中有一個參數:admission_control,他的值爲一串用逗號鏈接的 有序的 准入模塊列表,設置後,就可在對象被操做前執行必定順序的准入模塊調用。
模塊功能
AlwaysAdmit:容許全部請求
AlwaysDeny:禁止全部請求,多用於測試環境。
DenyExecOnPrivileged:它會攔截全部想在privileged container上執行命令的請求。若是本身的集羣支持privileged container,本身又但願限制用戶在這些privileged container上執行命令,那麼強烈推薦使用它。
ServiceAccount:這個plug-in將 serviceAccounts實現了自動化,若是想要使用ServiceAccount 對象,那麼強烈推薦使用它。
關於serviceAccount的描述以下: 一個serviceAccount爲運行在pod內的進程添加了相應的認證信息。當準入模塊中開啓了此插件(默認開啓),那麼當pod建立或修改時他會作一下事情:
若是pod沒有serviceAccount屬性,將這個pod的serviceAccount屬性設爲「default」;
確保pod使用de serviceAccount始終存在;
若是LimitSecretReferences 設置爲true,當這個pod引用了Secret對象卻沒引用ServiceAccount對象,棄置這個pod;
若是這個pod沒有包含任何ImagePullSecrets,則serviceAccount的ImagePullSecrets被添加給這個pod;
若是MountServiceAccountToken爲true,則將pod中的container添加一個VolumeMount 。
SecurityContextDeny:這個插件將會將使用了 SecurityContext的pod中定義的選項所有失效。SecurityContext 在container中定義了操做系統級別的安全設定(uid, gid, capabilities, SELinux等等)。
ResourceQuota:它會觀察全部的請求,確保在namespace中ResourceQuota對象處列舉的container沒有任何異常。 若是在kubernetes中使用了ResourceQuota對象,就必須使用這個插件來約束container。推薦在admission control參數列表中,這個插件排最後一個。
LimitRanger:他會觀察全部的請求,確保沒有違反已經定義好的約束條件,這些條件定義在namespace中LimitRange對象中。若是在kubernetes中使用LimitRange對象,則必須使用這個插件。
NamespaceExists:它會觀察全部的請求,若是請求嘗試建立一個不存在的namespace,則這個請求被拒絕。
推薦插件順序
--admission_control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount, ResourceQuota
1. API Server的初始化參數中設置了一些與權限認證相關的默認屬性:
安全監聽端口: SecurePort: 8443 讀/寫 權限,支持x509安全證書和x509私鑰認證
非安全監聽端口:InsecurePort: 8080 沒有用戶身份認證和受權,有讀/寫 權限
受權模式: AuthorizationMode: "AlwaysAllow",
准入控制插件: AdmissionControl: "AlwaysAdmit"
2. API Server啓動時能夠設置與權限認證相關的參數:
--insecure_port 自定義非安全監聽端口
--secure_port 自定義安全監聽端口
--tls_cert_file 設置安全證書文件
--tls_private_key_file 設置私鑰文件
--cert_dir 安全證書文件和私鑰文件被設置時,此屬性忽略。安全證書文件和私鑰文件未設置時,apiserver會自動爲該
端口綁定的公有IP地址分別生成一個自注冊的證書文件和密鑰並將它們存儲在/var/run/kubernetes
下
--service_account_key_file 服務帳號文件,包含x509 公私鑰
--client_ca_file client證書文件
--token_auth_file token文件
--basic_auth_file 基本信息認證文件
--authorization_mode 受權模式
--ahtuorization_policy_file 受權文件
--admission_control 准入控制模塊列表
--admission_control_config_file 准入控制配置文件
3. 解析入參,進行認證信息提取:
公 私鑰文件設置:查看ServerAccountKeyFile是否已指定,若是未指定,而且TLSPrivateKeyFile被指定,則判斷 TLSPrivateKeyFile中是否包含有效的RSA key,包含時,將TLSPrivateKeyFile做爲ServerAccountKeyFile。
身份認證信息提取:從參 數設置的CSV文件中取出username,userID,password(或者token)封裝成map結構,key爲username,value 爲三種屬性的struct。從basicAuthFile, clientCAFile, tokenFile, serviceAccountKeyFile(serviceAccountLookup) 中取user信息,獲得一個驗證信息的map數組。
授 權信息提取:讀取設置的受權文件,解析字符串,返回受權信息數組。(包含 username,group,resource,read only,namespace)Make the "cluster" Kinds be one API group (minions, bindings,events,endpoints)。The "user" Kinds are another (pods,services,replicationControllers,operations)。
准入控制插件:獲取全部插件名,返回准入控制接口(執行全部插件)
4. 將身份認證信息、受權信息、准入控制插件做爲Master的配置,New Master。
5. 請求認證:
調apiserver的NewRequestAttributeGetter方法,從請求中提取受權信息,調用WithAuthorizationCheck方法(受權驗證)。
調 用handler的NewRequestAuthenticator方法,Request中提取authencate信息,調用 AuthenticateRequest方法(對client certificates,token,basic auth分別有不一樣的驗證方法)。
身份認證:
token認證,請求時,在請求頭中加入 Authorization:bearer token字符串。CSV文件中,三列分別爲 token,username,userid。當CSV中有與請求的Authorization匹配行時,認證成功。
basic auth認證,請求時,在請求頭中加入 Authorization:basic base64編碼的user:password字符串。CSV文件中,三列分別爲 password,username,userid。當CSV文件中有與請求的Ahtuorization匹配行時,認證成功。
證書校驗:
API Server啓動時,指定服務端數字證書和密鑰(若是不指定,會在server啓動時自動生成),指定客戶端ca文件,server啓動時,會解析ca文 件,遍歷其中的cert,加入certpool。在Server的TLSConfig中指定認證模式:目前使用的是 RequestClientCert(不強制認證,無認證時不拒絕鏈接,容許其餘認證),此外還有其餘認證模式 requireAndVerifyClientCert(強制校驗)。使用ListenAndServeTLS(將服務端數字證書和密鑰做爲參數)監聽在 安全端口。
啓動server:指定token驗證文件、受權方式、受權文件
./_output/local/bin/linux/amd64/canary-apiserver --logtostderr=true --log-dir=/tmp --v=4 --etcd_servers=http://127.0.0.1:4001 --insecure_bind_address=127.0.0.1 --insecure_port=8088 --secure_port=8442 --kubelet_port=10250 --service-cluster-ip-range=10.1.1.0/24 --allow_privileged=true --runtime-config="api/v1beta3=false" --redis-addr=localhost:6379 --profiling=true --token_auth_file=token.csv --authorization_mode=ABAC --authorization_policy_file=abac.csv
Token文件內容:
abcdef,hankai,123456
abcdefg,hk,123457
abcd,admin,1234
abc,hhh,111
受權文件內容:
{「user」:」admin」}
{「user」:」hankai」,」readonly」:true}
{「user」:」hhh」,」resource」:」apps」}
{「user」:」hk」,」readonly」:true,」resource」:」namespaces」}
驗證:admin(有讀寫全部resource的權限)
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abcd" -k https://10.57.104.59:8442/api/v1/apps
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abcd" -k https://10.57.104.59:8442/api/v1/namespaces
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abcd" -d@'n1.json' -k https://10.57.104.59:8442/api/v1/namespaces
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abcd" -d@'app_demo1.json' -k https://10.57.104.59:8442/api/v1/apps
驗證 hankai (只有讀權限GET)
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abcdef" -d@'app_demo1.json' -k https://10.57.104.59:8442/api/v1/apps forbidden
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abcdef" -d@'n1.json' -k https://10.57.104.59:8442/api/v1/namespaces forbidden
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abcdef" -k https://10.57.104.59:8442/api/v1/namespaces
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abcdef" -k https://10.57.104.59:8442/api/v1/apps
驗證 hk (只有對namespaces的GET權)
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abcdefg" -k https://10.57.104.59:8442/api/v1/apps
forbidden
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abcdefg" -k https://10.57.104.59:8442/api/v1/namespaces
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abcdefg" -d@'n1.json' -k https://10.57.104.59:8442/api/v1/namespaces forbidden
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abcdefg" -d@'app_demo1.json' -k https://10.57.104.59:8442/api/v1/apps forbidden
驗證hhh(擁有對apps的讀寫權)
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abc" -d@'app_demo1.json' -k https://10.57.104.59:8442/api/v1/apps
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abc" -k https://10.57.104.59:8442/api/v1/apps
curl -X GET -H "Content-Type: application/json" -H "Authorization: bearer abc" -k https://10.57.104.59:8442/api/v1/namespaces
forbidden
curl -X POST -H "Content-Type: application/json" -H "Authorization: bearer abc" -d@'n1.json' -k https://10.57.104.59:8442/api/v1/namespaces
forbidden
注:後續只須要在abac.csv文件的每列中,指定namespace,就能夠實現user對指定namespace的操做權限。
新增:TSL 客戶端證書認證
使用自生成證書測試:使用openssl生成server.crt,server.key,ca.key,ca.crt。Server啓動時,傳入 --tls_cert_file=server.crt --tls_private_key_file=server.key --client_ca_file=ca.crt
./_output/local/bin/linux/amd64/canary-apiserver --logtostderr=true --log-dir=/tmp --v=4 --etcd_servers=http://127.0.0.1:4001 --insecure_bind_address=7.0.0.1 --insecure_port=8088 --secure_port=8442 --kubelet_port=10250 --service-cluster-ip-range=10.1.1.0/24 --allow_privileged=true --runtime-config="api/v1beta3=false" --redis-addr=localhost:6379 --profiling=true --tls_cert_file=server.crt --tls_private_key_file=server.key --client_ca_file=ca.crt --token_auth_file=token.csv --authorization_mode=ABAC --authorization_policy_file=abac.csv
請求時,經過-cacert 指定客戶端證書 (能夠經過修改opnessl的配置文件指定客戶端證書的路徑,或者瀏覽器中導入客戶端證書) curl -X GET --cacert ca.crt -H "Content-Type: application/json" -H "Authorization: bearer abcd" -k https://10.57.104.59:8442/api/v1/apps 便可實現認證。