Kubernetes的密钥是否真的安全?
这篇文章是 Kubernetes Advent Calendar 第12天的文章。
概述
在指定Kubernetes的Pod中的密码或令牌时,请使用Secret。(而且请使用它)
请注意不要将含有敏感信息的容器上传到DockerHub等地,以避免陷入麻烦的情况。
嗯,這篇文章的主題是”Kubernetes的Secret真的安全嗎”。
確實,在Kubernetes上作為一個功能存在,但它真的能夠確保安全性嗎?
而且,如果節點受到攻擊,Secret的數據能夠保護到什麼程度呢?
希望能够根据官方文件介绍当前的秘密情况。虽然作者还很菜,可能会有错误,但还请用温暖的眼光和友善的评论来指出,谢谢!
这是一个关于”Secret”的问题。
由于参考网站更加详细,所以详细内容请转至该网站。
总体来说,大致如下。
-
- 作成したsecretはKubernetesのetcd上にplain-text形式で保存される
-
- Nodeではpodのtmpfs上に保存される
- Node Authorization機能(v1.7〜)により、Node は割り当てられた Pod が参照する Secret 以外にはアクセスできない。
总结一下,原始数据存储在etcd上,而不存储在Master节点上。只有引用密钥的Pod所在的Node节点上的tmpfs上存在该数据(数据不保存在磁盘上)。通信采用https协议(etcd/Master/Node之间的通信均采用https)。※ 如果正确设置了。
看起来相当安全。
请您参照以下内容:
官方文档(https://kubernetes.io/docs/concepts/configuration/secret/)
Secret(https://ubiteku.oinker.me/2017/03/01/kubernetes-secrets/)
节点授权(https://qiita.com/tkusumi/items/f6a4f9150aa77d8f9822)
Secret的保密性
不过,Secret以明文形式存储在etcd上让人担忧。
虽然官方文档中提到为了安全起见需要正确设置etcd的访问控制,但是否可以更进一步提高安全性呢?
从Kubernetes 1.7开始,已经开发出了对etcd进行加密的功能。
使用这个功能可以更安全地管理Secret。
截止2017年12月(v1.8),这仍然是一个alpha级别的功能。
官方文档(https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/)
查询etcd的内容方法
现在开始验证实机。在使用etcd的加密功能之前,先来确认一下,如果什么都不做,密码等是否会以原始数据的形式保存。
确认创建一个Kubernetes集群。
使用Vagrant和kubeadm快速创建用于验证的Kubernetes集群吧。
※很抱歉,这只是为了验证而已,如果在读者的环境中无法运行的话,请原谅(大概在重启虚拟机后无法正常运行←)。
vagrant box add centos7 https://github.com/holms/vagrant-centos7-box/releases/download/7.1.1503.001/CentOS-7.1.1503-x86_64-netboot.box
vagrant up (※Vagrantfileは下記参照)
Vagrant.configure("2") do |config|
config.vm.box = "centos7"
config.vm.network "private_network", ip: "192.168.33.10"
config.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
end
end
使用SSH连接到虚拟机后的步骤。
[vagrant@localhost ~]# sudo su -
[root@localhost ~]# yum update -y && yum install -y docker etcd
[root@localhost ~]# swapoff -a
[root@localhost ~]# systemctl stop firewalld && systemctl disable firewalld
[root@localhost ~]# sysctl -w net.bridge.bridge-nf-call-iptables=1
[root@localhost ~]# cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://yum.kubernetes.io/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
[root@localhost ~]# yum install -y kubelet kubeadm kubectl
[root@localhost ~]# systemctl enable docker && systemctl start docker
[root@localhost ~]# systemctl enable kubelet
[root@localhost ~]# systemctl enable etcd && systemctl start etcd
[root@localhost ~]# cat <<EOF > etcd-conf.yaml
apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
api:
advertiseAddress: 192.168.33.10
etcd:
endpoints:
- http://127.0.0.1:2379
networking:
podSubnet: 10.244.0.0/16
EOF
[root@localhost ~]# kubeadm init --config=etcd-conf.yaml
[root@localhost ~]# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml --kubeconfig=/etc/kubernetes/admin.conf
[root@localhost ~]# kubectl taint node localhost.localdomain node-role.kubernetes.io/master --kubeconfig=/etc/kubernetes/admin.conf
创建秘密数据
接下来,在集群中创建一个样本的密钥。
密钥可以是任意的,但是这次我们将使用password: “ilovejapan”。
echo "ilovejapan" | base64
vi secret.yaml
※yamlファイルは以下を参照
kubectl create -f secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
password: aWxvdmVqYXBhbgo=
确认etcd上的数据
在etcd上确认数据的方法有两种:
(1) 直接查看二进制文件
hexdump -C /var/lib/etcd/default.etcd/member/snap/db | grep "ilovejapan"
000dc2b0 6f 72 64 12 0b 69 6c 6f 76 65 6a 61 70 61 6e 0a |ord..ilovejapan.|
用etcdctl命令查看
ETCDCTL_API=3 etcdctl get --prefix /registry/secrets/default/mysecret | hexdump -C
00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret|
00000010 73 2f 64 65 66 61 75 6c 74 2f 6d 79 73 65 63 72 |s/default/mysecr|
00000020 65 74 0a 6b 38 73 00 0a 0c 0a 02 76 31 12 06 53 |et.k8s.....v1..S|
00000030 65 63 72 65 74 12 70 0a 4d 0a 08 6d 79 73 65 63 |ecret.p.M..mysec|
00000040 72 65 74 12 00 1a 07 64 65 66 61 75 6c 74 22 00 |ret....default".|
00000050 2a 24 32 32 39 35 65 31 39 63 2d 64 34 32 38 2d |*$2295e19c-d428-|
00000060 31 31 65 37 2d 61 33 32 35 2d 30 38 30 30 32 37 |11e7-a325-080027|
00000070 36 62 35 37 38 38 32 00 38 00 42 08 08 ea f8 f4 |6b57882.8.B.....|
00000080 d0 05 10 00 7a 00 12 17 0a 08 70 61 73 73 77 6f |....z.....passwo|
00000090 72 64 12 0b 69 6c 6f 76 65 6a 61 70 61 6e 0a 1a |rd..ilovejapan..|
000000a0 06 4f 70 61 71 75 65 1a 00 22 00 0a |.Opaque.."..|
000000ac
etcd的加密
让我们尝试使用etcd上的任意目录(※)进行加密功能。
这个功能大概可以这样说,就是使用yaml格式来写加密方式和对称密钥的密钥文件,然后让apiserver读取它。
※:从etcd v3开始,目录的概念已经消失了,所以严格来说不是目录。
为了使用etcd的加密功能,需要在kube-apiserver上使用–experimental-encryption-provider-config=”config文件”选项来启动。
对于kubeadm,为了将config文件传递给apiserver,还需挂载HostPath。
本次密码是根据官方网站的指示使用以下命令创建的。
head -c 32 /dev/urandom | base64
yaml的格式如下所示。
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ""
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --experimental-encryption-provider-config=/etc/kubernetes/enc-conf.yaml
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- --secure-port=6443
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota
- --requestheader-allowed-names=front-proxy-client
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --allow-privileged=true
- --advertise-address=192.168.33.10
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --enable-bootstrap-token-auth=true
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --requestheader-username-headers=X-Remote-User
- --requestheader-group-headers=X-Remote-Group
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --insecure-port=0
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --authorization-mode=Node,RBAC
- --etcd-servers=http://127.0.0.1:2379
image: gcr.io/google_containers/kube-apiserver-amd64:v1.8.4
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /healthz
port: 6443
scheme: HTTPS
initialDelaySeconds: 15
timeoutSeconds: 15
name: kube-apiserver
resources:
requests:
cpu: 250m
volumeMounts:
- mountPath: /etc/kubernetes
name: k8s-conf
readOnly: true
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/pki
name: ca-certs-etc-pki
readOnly: true
hostNetwork: true
volumes:
- hostPath:
path: /etc/kubernetes
type: DirectoryOrCreate
name: k8s-conf
- hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
name: k8s-certs
- hostPath:
path: /etc/ssl/certs
type: DirectoryOrCreate
name: ca-certs
- hostPath:
path: /etc/pki
type: DirectoryOrCreate
name: ca-certs-etc-pki
status: {}
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: cSNmGdfrr/IZeYN9npkl3F7/ScQcer2We9VxmM4X5ww=
- identity: {}
然后重新启动 kube-apiserver。
systemctl restart kubelet
当我再次尝试检查etcd的内容时……
ETCDCTL_API=3 etcdctl get --prefix /registry/secrets/default/mysecret | hexdump -C
00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret|
00000010 73 2f 64 65 66 61 75 6c 74 2f 6d 79 73 65 63 72 |s/default/mysecr|
00000020 65 74 0a 6b 38 73 3a 65 6e 63 3a 61 65 73 63 62 |et.k8s:enc:aescb|
00000030 63 3a 76 31 3a 6b 65 79 31 3a 55 55 5d 34 1e 5d |c:v1:key1:UU]4.]|
00000040 72 67 0e be ef 16 5b d3 0c 08 36 42 2f 7c 92 1b |rg....[...6B/|..|
00000050 70 29 1f 67 0b 79 9c 29 2d 4c 1c ef 8a 7b 66 2b |p).g.y.)-L...{f+|
00000060 0a a8 8a 1e 5e 72 e5 6a 50 52 62 1e 8a 80 58 6d |....^r.jPRb...Xm|
00000070 99 5c d3 21 50 a1 08 14 b5 be 7d a1 1f 73 78 22 |.\.!P.....}..sx"|
00000080 7d c4 33 b5 eb 8a f1 dd 32 e2 c3 cc 1f 64 e0 f1 |}.3.....2....d..|
00000090 6d aa 49 56 31 93 79 19 cf 53 dc 0b db a7 60 8b |m.IV1.y..S....`.|
000000a0 a7 ad ad 33 2a 5d 3a 1b e2 15 91 9d d5 e3 c5 ea |...3*]:.........|
000000b0 fc 6b d4 21 33 a0 2e 80 3f 1d a8 0b f5 b4 98 98 |.k.!3...?.......|
000000c0 1c 2a 00 1d d7 e6 55 f9 88 35 c2 c6 a3 b3 20 63 |.*....U..5.... c|
000000d0 e8 2e eb af 1f bc 6b 92 d8 72 0a |......k..r.|
000000db
无法看到无事密码了。
综合评价
如果正确设置,Kubernetes的Secret功能非常丰富且看起来很安全。
但是Kubernetes不仅仅是设置Secret,设置正确是非常困难的……
如果能更简单地设计一个安全的集群就太好了。