深入理解k8s中的訪問控制(認證、鑑權、審計)流程
password,user,uid,"group1,group2,group3"發起請求時在HTTP中新增頭即可:
Authorization: Basic BASE64ENCODED(USER:PASSWORD)3、clientCA認證 X509認證是Kubernetes元件間預設使用的認證方式,同時也是kubectl客戶端對應的kube-config中經常使用到的訪問憑證。它是一個比較安全的方式。 首先訪問者會使用由叢集CA簽發的,或是新增在apiserver配置中的授信CA簽發的客戶端證書去訪問apiserver。apiserver在接收到請求後,會進行TLS的握手流程。 除了驗證證書的合法性,apiserver還會校驗客戶端證書的請求源地址等資訊,開啟雙向認證。 進行證書籤發的步驟: (1)建立根CA
cat << EOF | tee ca-config.json { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "expiry": "87600h", "usages": [ "signing", "key encipherment", "server auth", "client auth" ] } } } } EOF
其中:
- profiles:指定不同的過期時間、使用場景等引數。檔案中可以定義多個,分別後續在簽名證書時使用某一個
- signing:表示該證書可用於簽名其它證書,生成的ca.pem證書中CA=TRUE
- key encipherment:表示金鑰用法為金鑰加密
- server auth:表示client可以用該CA 對server提供的證書進行驗證
- client auth:表示server可以用該CA對client提供的證書進行驗證
cat << EOF | tee ca-csr.json { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "Shenzhen", "ST": "Shenzhen", "O": "k8s", "OU": "System" } ] } EOF
其中:
- CN:Common Name,用於從中提取該欄位作為請求的使用者名稱
- C:Country, 國家
- ST: State,州,省
- L: Locality,地區,城市
- O: Organization Name, 用於從中提前該欄位作為請求使用者所屬的組
- OU: Organization Unit Name,組織單位名稱,公司部門
cfssl gencert -initca ca-csr.json | cfssljson -bare ca執行後生成檔案ca.csr、ca-key.pem、ca.pem (2)簽發其它系統元件的證書 Kubernetes叢集中所有系統元件與apiserver通訊用到的證書,其實都是由叢集根CA來簽發的。 ①如kube-proxy對應的csr檔案
cat << EOF | tee kube-proxy-csr.json { "CN": "system:kube-proxy", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "L": "Shenzhen", "ST": "Shenzhen", "O": "k8s", "OU": "System" } ] } EOF
使用根CA簽署證書:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy執行後生成檔案kube-proxy.csr、kube-proxy-key.pem、kube-proxy.pem ②kubelet啟動時實際需要指定兩個配置檔案 --kubeconfig指定的是kube-config檔案,其中內建了叢集根CA公鑰以及自己作為客戶端的公鑰和私鑰 --config指定的是kubelet的配置,格式如下:
kind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 address: xxx.xxx.xxx.xxx port: 10250 readOnlyPort: 10255 cgroupDriver: cgroupfs clusterDNS: ["10.0.0.2"] clusterDomain: cluster.local. failSwapOn: false authentication: anonymous: enabled: true
kubelet元件在工作時,採用主動的查詢機制,即定期請求apiserver 獲取自己所應當處理的任務,如哪些pod分配到了自己身上,從而去處理這些任務;同時kubelet自己還會暴露出兩個本身api的埠,用於將自己本身的私有api暴露出去,這兩個埠分別是該配置檔案中指定的10250與10255。
對於10250埠,kubelet會在其上採用TLS加密以提供適當的鑑權功能;對於10255埠,kubelet會以只讀形式暴露元件本身的私有api,並且不做鑑權處理。 因此,kubelet上實際上有兩個地方用到證書,一個是用於與 API server通訊所用到的證書,另一個是該配置檔案中設定的kubelet的10250私有api埠需要用到的證書。 (3)簽發使用者的證書 ①首先開發人員需用通過OpenSSL等證書工具生成私鑰openssl genrsa -out test.key 2048②建立對應的x509 csr請求檔案(需要在subj欄位中指定user和group)
openssl req -new -key test.key -out test.csr -subj "/CN=xxxx/O=xxxx"③Kubernetes叢集本身就提供了證書籤發的API certificates.k8s.io/v1beta1。呼叫後,api-server會根據請求,以csr資源物件的形式建立對應的簽發請求。 例如,在叢集的建立過程中,像kubeadm這樣的叢集安裝工具,也會基於不同的csr簽發請求呼叫api-server對應介面,建立不同的csr資源物件。 使用者可以通過API建立K8s csr例項並等待管理員審批。
cat <<EOF | kubectl apply -f - apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: xxxx spec: groups: - system:authenticated request: $(cat test.csr | base64 | tr -d "\n") usages: - client auth EOF
PS:request中是base64編碼的csr檔案
剛開始建立的簽發例項都會處於pending狀態:NAME AGE REQUESTOR CONDITION xxxx 10s admin Pending直到有許可權的管理員進行審批後,這個csr才會處於approved狀態,請求對應的證書就會被簽發
# kubectl certificate approve john certificatesigningrequest.certificates.k8s.io/xxxx approved證書以base64存在csr資源物件的.status.certificate中,配合私鑰、CA公鑰即可製作kubeconfig檔案 叢集管理員也可以直接讀取叢集根CA,並通過x509 csr請求檔案簽發證書。簽發示例如下:
# openssl x509 -req -in test.csr -CA CA_LOCATION/ca.crt -Cakey CA_LOCATION/ca.key -Cacreateserial -out test.crt -days 365命令中需要指明csr和ca.crt的檔案路徑,以及簽發證書的過期時間資訊 ④檢視kubeconfig配置
# kubectl config view
kubectl預設會從$HOME/.kube目錄下查詢檔名為config 的檔案,也可以通過設定環境變數KUBECONFIG或者通過設定--kubeconfig去指定其它kubeconfig檔案。
檔案格式為:
{ "apiVersion": "v1", "kind": "Config", "preferences": {}, "clusters": [ { "cluster": { "certificate-authority": "server": "https://ip:6443" }, "name": {cluster-name} } ], "contexts": [ { "context": { "cluster": {cluster-name}, "user": {user-name} }, "name": {context-name} } ], "users": [ { "name": {user-name}, "user": { "client-certificate": "client-key": } } ] "current-context": {context-name}, }
若想要用base64編碼資料代替認證檔案,需要新增字尾-data,將 certificate-authority、client-certificate、client-key改為certificate-authority-data、client-certificate-data、client-key-data
從config檔案還原證書的方法:# grep 'client-key-data' /etc/kubernetes/admin.conf | head -n 1 | awk '{print $2}' | base64 -d
# grep 'client-certificate-data' /etc/kubernetes/admin.conf | head -n 1 | awk '{print $2}' | base64 -d⑤下載叢集ca公鑰檔案ca.pem,使用kubectl新增叢集連線資訊
# kubectl config set-cluster xxx --certificate-authority=ca.pem --embed-certs=true --server=https://ip:6443⑥使用kubectl設定kubeconfig的users配置段資訊,需要將使用者祕鑰資訊加入kubectl配置中
# kubectl config set-credentials {user-name} --client-certificate=test.crt --client-key=test.key --embed-certs=true⑦新增新的context入口到kubectl配置中
# kubectl config set-context {context-name} --cluster={cluster-name} --user={user-name}⑧多叢集config的合併和切換
# export KUBECONFIG=file1:file2:file3
# kubectl config view --merge --flatten > ~/.kube/all-config
# export KUBECONFIG = ~/.kube/all-config⑨檢視和切換上下文
# kubectl config get-contests
# kubectl config use-context {your-contexts}4、TokenAuth認證 啟動apiserver時通過--token-auth-file引數啟用TokenAuth認證。 AUTH_FILE(Static Password file)是一個CSV檔案,檔案格式為:
token,user,uid,"group1,group2,group3"發起請求時在HTTP中新增頭即可:
Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb752695、ServiceAccountAuth認證 serviceaccount是k8s中唯一能夠通過API方式管理的apiserver訪問憑證,通常用於pod中的業務程序與apiserver的互動 ServiceAccount解決Pod在叢集裡面的身份認證問題,認證使用的授權資訊存在secret裡面(由SecretAccount Controller自行建立)。 當一個namespace建立完成後,會同時在該namespace下生成名為default的serviceaccount和對應Secret:
apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: "2019-11-19T03:07:32Z" name: default namespace: default resourceVersion: "191" selfLink: /api/v1/namespaces/default/serviceaccounts/default uid: b2322727-08d5-4095-acbe-1afee4fb5e6c secrets: - name: default-token-nfdr4對應的Secret裡: data欄位有兩塊資料:ca.crt用於對服務端的校驗,token用於Pod的身份認證,它們都是用base64編碼過的。 metadata裡annotations欄位表明了關聯的ServiceAccount資訊(被哪個ServiceAccount使用)。 type欄位表明了該Secret是service-account-token型別
apiVersion: v1 data: ca.crt: LS0tLS1... namespace: ZGVmYXVsdA== token: ZXlKaG... kind: Secret metadata: annotations: kubernetes.io/service-account.name: default kubernetes.io/service-account.uid: b2322727-08d5-4095-acbe-1afee4fb5e6c creationTimestamp: "2019-11-19T03:07:32Z" name: default-token-nfdr4 namespace: default resourceVersion: "190" selfLink: /api/v1/namespaces/default/secrets/default-token-nfdr4 uid: cbb919a4-6309-43c0-ac0b-566e30e9b116 type: kubernetes.io/service-account-token此外,使用者也可以通過api建立其它名稱的ServiceAccount,並在該namespace的Pod的.spec.ServiceAccount下指定,預設是default。 Pod建立的時候,Admission Controller會根據指定的ServiceAccount把對應secret的ca.crt和token檔案掛載到固定目錄/var/run/secrets/kubernetes.io/serviceaccount下。
volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: default-token-jbcp7 readOnly: truepod要訪問叢集的時候,預設利用Secret其中的token檔案來認證Pod的身份,利用ca.crt校驗服務端 預設token的認證資訊為: - Group:system:serviceaccounts:[namespace-name] - User:system:serviceaccount:[namespace-name]:default Pod身份被認證合法後,其許可權需要通過RBAC來配置,預設只有資源GET許可權 PS:如果是在Pod建立過程中,發現指定的ServiceAccount不存在,則該Pod建立過程會被終止。 PS:對於已經建立的Pod,不能更新其已經掛載的ServiceAccount內容。 6、Bootstrap Token認證 如果節點多起來,為每個節點單獨簽署證書將是一件非常繁瑣的事情 TLS bootstrapping的功能就是讓kubelet先使用一個預定的低許可權使用者連線到apiserver,向apiserver申請證書,證書由apiserver動態簽署 在配合RBAC授權模型下的工作流程大致如下所示: TLS bootstrapping下kubelet發起的CSR請求大致分為以下三種: nodeclient:kubelet以O=system:nodes、CN=system:node:(node name)形式發起的CSR請求(僅在第一次啟動時產生) selfnodeclient:kubelet發起的更新自己的作為client的證書的CSR請求(與上一個證書有相同的O、CN) selfnodeserver:kubelet發起的更新自己的作為server的證書(即kubelet 10250 api埠證書)的CSR請求 ①使用TLS Bootstrapping Token時的配置流程: (1)建立TLS Bootstrapping Token
# head -c 16 /dev/urandom | od -An -t x | tr -d ' '(2)修改使用者的描述檔案token.csv(相當於預設的使用者配置),基本格式為Token,user,uid,group:
8f01b7072246e0f3409d54e379c8699f
8f01b7072246e0f3409d54e379c8699f,kubelet-bootstrap,10001,"system:kubelet-bootstrap"(3)在apiserver配置中新增--enable-bootstrap-token-auth開啟TLS bootstrapping功能,通過--token-auth-file引數指定token.csv檔案,apiserver啟動時會將其載入,相當於在叢集內建立了這個使用者。 (4)kubelet-bootstrap使用者沒有任何許可權(包括建立CSR請求),需要建立一個ClusterRoleBinding,將預設使用者kubelet-bootstrap使用者與內建的ClusterRole system:node-bootstrapper繫結到一起,使其能夠發起 CSR 請求
# kubectl create clusterrolebinding kubelet-bootstrap \否則kubelet會報401無權訪問apiserver的錯誤 (4)建立kubelet的配置檔案bootstrapping.kubeconfig
--clusterrole=system:node-bootstrapper \
--user=kubelet-bootstrap
BOOTSTRAP_TOKEN=01f6717d648e3e7e71282a9632dd99ab KUBE_APISERVER="https://132.224.197.35:6443"
執行命令:
# kubectl config set-cluster kubernetes \ --certificate-authority=./ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=bootstrap.kubeconfig # kubectl config set-credentials kubelet-bootstrap \ --token=${BOOTSTRAP_TOKEN} \ --kubeconfig=bootstrap.kubeconfig # kubectl config set-context default \ --cluster=kubernetes \ --user=kubelet-bootstrap \ --kubeconfig=bootstrap.kubeconfig # kubectl config use-context default --kubeconfig=bootstrap.kubeconfigkubelet-bootstrap使用者的Token和apiserver的使用的CA證書被寫入了該配置檔案中 首次請求時,kubelet使用配置檔案中的apiserver CA證書與apiserver建立TLS通訊,使用配置檔案中的使用者Token向apiserver宣告自己的RBAC授權身份。 (5)啟動kubelet時需要指定--kubeconfig和--bootstrap-kubeconfig(兩種身份),指定--cert-dir用來存放所有證書 (6)在kubelet首次啟動後,如果使用者Token沒問題,並且RBAC也做了相應的設定,那麼此時在叢集內應該能看到kubelet發起的CSR 請求。出現CSR請求後,可以使用kubectl手動簽發kubelet的證書 (7)當成功簽發證書後,目標節點的 kubelet 會將證書寫入到--cert-dir選項指定的目錄中 kubelet-client.crt、kubelet-client.key:kubelet與apiserver通訊所使用的證書 kubelet.crt、kubelet.key:用於kubelet的10250埠做鑑權使用(這個證書是個獨立於apiserver CA的自籤CA,並且刪除後kubelet會重新生成它) 配置controller manager自動簽署證書 kubelet發起的CSR請求都是由controller manager來做實際簽署的 (1)kubelet啟動時增加--feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true引數,則證書即將到期時會自動發起一個renew自己證書的CSR請求 配置了--feature-gates=RotateKubeletClientCertificate=true後,kubelet首次啟動時仍會使用kubelet-client.crt證書與apiserver通訊。續期請求被批准後會生成一個kubelet-client-時間戳.pem,kubelet-client-current.pem檔案則始終軟連線到最新的真實證書檔案 配置了--feature-gates=RotateKubeletServerCertificate=true後不再生成kubelet.crt,改為生成kubelet-server-時間戳.pem,kubelet-server-current.pem檔案則始終軟連線到最新的真實證書檔案 (2)controller manager啟動時增加--feature-gates=RotateKubeletServerCertificate=true引數,則會在kubelet發起證書請求的時候自動幫助其簽署證書 PS:如果不配置該引數,則即使配置了相關的RBAC規則,也只會自動批准kubelet client的更新證書請求 此外,還需要配置RBAC 規則,保證 controller manager只對kubelet發起的特定CSR請求自動批准 (3)針對kubelet發起的3種CSR請求建立3種對應的ClusterRole:
# A ClusterRole which instructs the CSR approver to approve a user requesting node client credentials. kind:ClusterRole apiVersion:rbac.authorization.k8s.io/v1 metadata: name:approve-node-client-csr rules: -apiGroups:["certificates.k8s.io"] resources:["certificatesigningrequests/nodeclient"] verbs:["create"] --- # A ClusterRole which instructs the CSR approver to approve a node renewing its own client credentials. kind:ClusterRole apiVersion:rbac.authorization.k8s.io/v1 metadata: name:approve-node-client-renewal-csr rules: -apiGroups:["certificates.k8s.io"] resources:["certificatesigningrequests/selfnodeclient"] verbs:["create"] --- # A ClusterRole which instructs the CSR approver to approve a node requesting a serving cert matching its client cert. kind:ClusterRole apiVersion:rbac.authorization.k8s.io/v1 metadata: name:approve-node-server-renewal-csr rules: -apiGroups:["certificates.k8s.io"] resources:["certificatesigningrequests/selfnodeserver"] verbs:["create"]PS:1.8後的apiserver自動建立了前兩條ClusterRole (4)將適當的ClusterRole繫結到 kubelet 自動續期時所所採用的使用者或者使用者組身上 自動批准kubelet的首次CSR請求(用於與apiserver通訊的證書):
# kubectl create clusterrolebinding node-client-auto-approve-csr --clusterrole=approve-node-client-csr --group=system:bootstrappers
自動批准 kubelet 後續 renew 用於與 apiserver 通訊證書的 CSR 請求:
# kubectl create clusterrolebinding node-client-auto-renew-crt --clusterrole=approve-node-client-renewal-csr --group=system:nodes自動批准 kubelet 發起的用於 10250 埠鑑權證書的 CSR 請求(包括後續 renew):
# kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=approve-node-server-renewal-csr --group=system:nodesPS:在 1.8 後kubelet需要增加--rotate-certificates引數,kubelet 才會自動過載新證書 PS:在 1.7 版本以後kube-controller-manager可以通過--experimental-cluster-signing-duration引數來設定簽署的證書有效時間,預設為8760h0m0s(1年)。 ②使用TLS Bootstrapping Token Secret時的配置流程: (1)生成token
echo "$(head -c 6 /dev/urandom | md5sum | head -c 6)"."$(head -c 16 /dev/urandom | md5sum | head -c 16)” 47f392.d22d04e89a65eb22Token 必須滿足 [a-z0-9]{6}\.[a-z0-9]{16} 格式;以 . 分割,前面的部分被稱作Token ID(可以暴露出去),後面的部分稱為Token Secret(需要保密) (2)建立Bootstrap Token Secret
apiVersion: v1 kind: Secret metadata: name: bootstrap-token-07401b namespace: kube-system type: bootstrap.kubernetes.io/token stringData: description: "The default bootstrap token generated by 'kubeadm init'." token-id: 47f392 token-secret: d22d04e89a65eb22 expiration: 2018-09-10T00:00:11Z usage-bootstrap-authentication: "true" usage-bootstrap-signing: "true" auth-extra-groups: system:bootstrappers:worker,system:bootstrappers:ingresstype 必須為 bootstrap.kubernetes.io/token name 必須為 bootstrap-token-<token id> usage-bootstrap-authentication、usage-bootstrap-signing 必須設定為 true expiration 欄位是可選的,如果設定則到期後將由 Controller Manager 中的 tokencleaner 自動清理 auth-extra-groups 也是可選的,令牌的擴充套件認證組,組必須以system:bootstrappers:開頭 (3)引導時,kubelet使用Token發起的請求其使用者名稱為system:bootstrap:<token id>,使用者組為system:bootstrappers 建立ClusterRoleBinding時要繫結到這個使用者或者組上 允許 system:bootstrappers 組使用者建立 CSR 請求:
# kubectl create clusterrolebinding kubelet-bootstrap \自動批准 system:bootstrappers 組使用者 TLS bootstrapping 首次申請證書的 CSR 請求:
--clusterrole=system:node-bootstrapper \
--group=system:bootstrappers
# kubectl create clusterrolebinding node-client-auto-approve-csr \自動批准 system:nodes 組使用者更新 kubelet 自身與 apiserver 通訊證書的 CSR 請求:
--clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient \
--group=system:bootstrappers
# kubectl create clusterrolebinding node-client-auto-renew-crt \
--clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient \
--group=system:nodes
自動批准 system:nodes 組使用者更新 kubelet 10250 api 埠證書的 CSR 請求:
# kubectl create clusterrolebinding node-server-auto-renew-crt \ --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeserver \ --group=system:nodes(4)Controller Manager需要新增引數--controllers=*,bootstrapsigner,tokencleaner以啟用 tokencleaner 和 bootstrapsigner (5)kubelet使用的kubeconfig檔案也要相應變化:
# kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/ssl/k8s-root-ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=bootstrap.kubeconfig # kubectl config set-credentials system:bootstrap:47f392 \ --token=47f392.d22d04e89a65eb22 \ --kubeconfig=bootstrap.kubeconfig # kubectl config set-context default \ --cluster=kubernetes \ --user=system:bootstrap:47f392 \ --kubeconfig=bootstrap.kubeconfig # kubectl config use-context default --kubeconfig=bootstrap.kubeconfig7、OIDC認證 所謂OIDC(OpenID Connect),就是先向identity provider獲取伺服器簽名的JSON Web Token (JWT) identity provider會提供access_token、id_token、refresh_token 使用kubectl時通過--token引數新增id_token,或者直接把它新增到kubeconfig檔案中,kubectl會把id_token新增到http頭裡 apiserver會通過證書確認JWT是否有效、確認JWT是否過期、身份是否合法等 使用OIDC認證,apiserver需要配置: --oidc-issuer-url:identity provider的地址 --oidc-client-id:client id,一般配成kubernetes --oidc-username-claim,如sub --oidc-groups-claim,如groups --oidc-ca-file:為identity provider簽名的CA公鑰 8、Webhook TokenAuth認證 當客戶端傳送的認證請求到達apiserver時,apiserver回撥鉤子方法,將驗證資訊傳送給遠端的Webhook伺服器進行認證,然後根據Webhook伺服器返回的狀態碼來判斷是否認證成功 通過指定如下引數啟用WebhookTokenAuth認證: --authentication-token-webhook-config-file:Webhook配置檔案描述瞭如何訪問遠端Webhook服務 --authentication-token-webhook-cache-ttl:快取認證時間,預設值為2分鐘 9、Anonymous認證 未被其他認證器拒絕的請求都可視為匿名請求,匿名使用者權值很低 apiserver通過指定--anonymous-auth引數啟用Anonymous認證,預設該引數值為true 認證流程之後,api-server會將請求中憑證中的使用者身份轉化為對應的User和Groups。在隨後的鑑權操作和審計操作流程中,api-server都會使用該使用者模型例項。 二、鑑權階段(Authorization) 採用RBAC判斷使用者是否有許可權進行請求中的操作。如果無權進行操作,api-server會返回403的狀態碼,並終止該操作 RBAC包含三個要素:
- Subjects:可以是開發人員、叢集管理員這樣的自然人,也可以是系統元件程序、Pod中的業務程序;
- API Resource:也就是請求對應的訪問目標,在Kubernetes叢集中指各類資源物件;
- Verbs:對應為請求物件資源可以進行哪些操作,如list、get、watch等。
# kube-apiserver -h | grep enable-admission-plugins --admission-control strings Admission is divided into two phases. In the first phase, only mutating admission plugins run. In the second phase, only validating admission plugins run. The names in the below list may represent a validating plugin, a mutating plugin, or both. The order of plugins in which they are passed to this flag does not matter. Comma-delimited list of: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, DefaultStorageClass, DefaultTolerationSeconds, DenyEscalatingExec, DenyExecOnPrivileged, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodPreset, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. (DEPRECATED: Use --enable-admission-plugins or --disable-admission-plugins instead. Will be removed in a future version.) --enable-admission-plugins strings admission plugins that should be enabled in addition to default enabled ones (NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota). Comma-delimited list of admission plugins: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, DefaultStorageClass, DefaultTolerationSeconds, DenyEscalatingExec, DenyExecOnPrivileged, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodPreset, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. The order of plugins in this flag does not matter.
可見,AC一共有幾十種,下面介紹一些常用的:
1、ResourceQuota和LimitRanger ResourceQuota可以限制namespace資源用量apiVersion: v1 kind: ResourceQuota metadata: name: ns-quota-cns-test namespace: cns-test spec: hard: pods: "4" requests.cpu: "1" requests.memory: 1Gi limits.cpu: "26" limits.memory: 2Gi scopeSelector: matchExpressions: - operator: Exists scopeName: NotBestEffortspec.hard:除了基礎的資源,還可以可以限制Pod數量 spec.scopeSelector:定義更豐富的索引能力,包括Terminating/Not Terminating、BestEffort/NotBestEffort、PriorityClass。 建立ResourceQuota後,如果使用者用超了資源,在提交Pod時,會收到一個forbidden的403錯誤,提示exceeded quota。但假如使用者提交沒有包含在這個ResourceQuota方案裡面的資源,還是能成功的。 在某個namespace下建立LimitRange,則會自動為該namespace下的容器新增request和limit 例如建立該LimitRange:
apiVersion: v1 kind: LimitRange metadata: name: mem-limit-range spec: limits: - default: memory: 512Mi defaultRequest: memory: 256Mi type: Container會自動為所在名稱空間下的容器新增256M的記憶體request和512M的記憶體limit 2、SecurityContextDeny SecurityContext用於限制容器的一個行為,保證系統和其他容器的安全。從粒度上又分為以下兩種: Container-level Security Context:僅應用到指定的容器 Pod-level Security Context:應用到Pod內所有容器以及Volume 該能力不是Kubernetes或者容器runtime本身的能力,而是kubernetes和runtime通過使用者的配置,最後下傳到核心裡,再通過核心的機制讓其生效。 Pod級別和容器級別配置SecurityContext的例子:
apiVersion: v1 kind: Pod metadata: name: security-context-demo spec: securityContext: runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 volumes: - name: sec-ctx-vol emptyDir: {} containers: - name: sec-ctx-demo image: busybox command: [ "sh", "-c", "sleep 1h" ] volumeMounts: - name: sec-ctx-vol mountPath: /data/demo securityContext: allowPrivilegeEscalation: falseSecurityContext設定項主要包括: (1)通過使用者ID和組ID來控制檔案訪問許可權; (2)SELinux:通過策略配置來控制使用者或者程序對檔案的訪問控制; (3)特權容器; (4)Capabilities:給特定程序來配置一個privileged能力; (5)AppArmor:通過一些配置檔案來控制可執行檔案的一個訪問控制權限(比如說一些埠的讀寫); (6)對系統呼叫的控制; (7)對子程序能否獲取比父親更多的許可權的一個限制 最後其實都是落到核心來控制它的一些許可權。 3、PodSecurityPolicy Pod Security Policies(PSP)是應用到叢集內部所有Pod以及Volume的安全策略 PSP的使用: (1)通過在apiserver的admission-plugin引數中新增PodSecurityPolicy開啟: (2)在叢集中建立PSP策略例項,支援的控制項包括: (3)配置策略與身份的RBAC策略繫結(大多數pod中使用的身份都是Serviceaccount) (4)啟動PSP後admission會強制要求pod在鑑權後找到至少一個對應的策略例項,因此最好設定一個叢集維度的全域性策略,同時針對指定namespace配置細化策略 (5)如果同時有多個PSP滿足許可權繫結關係,優先從非mutating(不改變Pod模型的策略)的滿足策略中按照例項name名字母排序選擇第一個 4、ValidatingAdmissionWebhook和MutatingAdmissionWebhook webhook就是一個HTTP回撥,接收API Server傳送的admissionReview請求,處理(驗證或修改)並返回admissionResponse。 使用步驟: (1)建立ValidatingWebhookConfiguration檔案和MutatingWebhookConfiguration檔案:
apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration metadata: name: validation-kube-webhook-cfg namespace: paas labels: app: paas-webhook webhooks: - name: nodeport.kube-webhook.cn clientConfig: service: name: paas-webhook-svc namespace: paas path: "/validating" caBundle: LS0tLS1... rules: - operations: [ "CREATE" ] apiGroups: ["apps", "extensions", ""] apiVersions: ["v1", "v1beta1"] resources: ["services"] namespaceSelector: matchLabels: paas-webhook: enabled其中rules定義了匹配規則,當發給apiserver的請求滿足該規則的時候,apiserver就會給clientConfig中配置的service傳送Admission請求。 (2)開發的webhook程式需要實現IWebHookServer介面:
type IWebHookServer interface { mutating(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionResponse validating(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionResponse Start() Stop() }例如,定義一個結構體:
type webHookServer struct { server *http.Server }實現這四個方法: ①開始
func (ws *webHookServer) Start() { ws.server.ListenAndServeTLS("", "") }②結束
func (ws *webHookServer) Stop() { glog.Infof("Got OS shutdown signal, shutting down wenhook server gracefully...") ws.server.Shutdown(context.Background()) }在main函式中呼叫go ws.Start()後,可以以如下方式阻塞,等待退出訊號後再呼叫ws.Stop()
signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) <-signalChan ws.Stop()③mutating負責修改 ④validating負責稽核 可以用一個req := ar.Request接下請求 req.Kind是metav1.GroupVersionKind(GVK結構體) 例如req.Kind.Kind是Service時的反序列化:
var service corev1.Service json.Unmarshal(req.Object.Raw, &service) resourceName, resourceNamespace, objectMeta = service.Name, service.Namespace, &service.ObjectMeta最後返回結果的資料結構:
type AdmissionResponse struct { UID types.UID `json:"uid" protobuf:"bytes,1,opt,name=uid"` Allowed bool `json:"allowed" protobuf:"varint,2,opt,name=allowed"` Result *metav1.Status `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"` Patch []byte `json:"patch,omitempty" protobuf:"bytes,4,opt,name=patch"` PatchType *PatchType `json:"patchType,omitempty" protobuf:"bytes,5,opt,name=patchType"` AuditAnnotations map[string]string `json:"auditAnnotations,omitempty" protobuf:"bytes,6,opt,name=auditAnnotations"` }返回示例:
allowed := true result = &metav1.Status{ Reason: "Unauthorized nodeport", } return &v1beta1.AdmissionResponse{ Allowed: allowed, Result: result, }
webhook可以做到很多事情,例如限制每個namespace使用的埠號、為每個Pod插入sidecar容器等。
參考資料:
[1] https://kubernetes.io/docs/home/
[2] https://edu.aliyun.com/roadmap/cloudnative
[3] https://mritd.me/2018/08/28/kubernetes-tls-bootstrapping-with-bootstrap-token/
[4] https://mritd.me/2018/01/07/kubernetes-tls-bootstrapping-note/
[5] 鄭東旭《Kubernetes原始碼剖析》