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,设置正确是非常困难的……
如果能更简单地设计一个安全的集群就太好了。

bannerAds