password,user,uid,"group1,group2,group3"
Authorization: Basic BASE64ENCODED(USER:PASSWORD)
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
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
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。
openssl genrsa -out test.key 2048
openssl req -new -key test.key -out test.csr -subj "/CN=xxxx/O=xxxx"
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檔案
NAME AGE REQUESTOR CONDITION
xxxx 10s admin Pending
# kubectl certificate approve john
certificatesigningrequest.certificates.k8s.io/xxxx approved
# openssl x509 -req -in test.csr -CA CA_LOCATION/ca.crt -Cakey CA_LOCATION/ca.key -Cacreateserial -out test.crt -days 365
# 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
# 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
# kubectl config set-cluster xxx --certificate-authority=ca.pem --embed-certs=true --server=https://ip:6443
# kubectl config set-credentials {user-name} --client-certificate=test.crt --client-key=test.key --embed-certs=true
# kubectl config set-context {context-name} --cluster={cluster-name} --user={user-name}
# 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}
token,user,uid,"group1,group2,group3"
Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269
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
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
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-jbcp7
readOnly: true
# head -c 16 /dev/urandom | od -An -t x | tr -d ' '
8f01b7072246e0f3409d54e379c8699f
8f01b7072246e0f3409d54e379c8699f,kubelet-bootstrap,10001,"system:kubelet-bootstrap"
# kubectl create clusterrolebinding kubelet-bootstrap \
--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.kubeconfig
# 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"]
# 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
# kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=approve-node-server-renewal-csr --group=system:nodes
echo "$(head -c 6 /dev/urandom | md5sum | head -c 6)"."$(head -c 16 /dev/urandom | md5sum | head -c 16)” 47f392.d22d04e89a65eb22
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:ingress
# kubectl create clusterrolebinding kubelet-bootstrap \
--clusterrole=system:node-bootstrapper \
--group=system:bootstrappers
# kubectl create clusterrolebinding node-client-auto-approve-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
# 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.kubeconfig
- 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一共有幾十種,下面介紹一些常用的:
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: NotBestEffort
apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
spec:
limits:
- default:
memory: 512Mi
defaultRequest:
memory: 256Mi
type: Container
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: false
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
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()) }
signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) <-signalChan ws.Stop()
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原始碼剖析》