- 7.5.1 介绍 Secret
- 7.5.2 默认令牌 Secret 的介绍
- 7.5.3 创建Secret
- 7.5.4 对比 ConfigMap 与 Secret
- 7.5.5 在 pod 中使用 Secret
- 7.5.6 实验:镜像拉取Secrets的Docker本地仓库registry实现
kubectl describe po kubia-manual
#配置imagePullSecrets重新创建pod,pod中的容器就能正常拉取镜像并运行了。
">registry开启安全认证后,在pod还未配置imagePullSecrets字段的时候,pod中的容器无法拉取镜像。kubectl create -f kubia-manual.yaml
kubectl describe po kubia-manual
#配置imagePullSecrets重新创建pod,pod中的容器就能正常拉取镜像并运行了。
7.5.1 介绍 Secret
Secret对象用于存储与分发配置中包含的敏感数据(如证书和私钥)。Secret结构与ConfigMap类似,均是键/值对的映射。Secret的使用方法也与ConfigMap相同,可以:
- 将 Secret 条目作为环境变量传递给容器 (不建议使用)
- 将 Secret 条目暴露为卷中的文件
Kubernetes通过仅仅将Secret分发到需要访问Secret的pod所在的节点来保障其安全性。另外,Secret只会存储在节点的内存中,永不写入物理存储,这样从节点上删除Secret时就不需要擦除磁盘了。
对于主节点本身 (尤其是etcd),Secret通常以非加密形式存储,这就需要保障主节点的安全从而确保存储在Secret中的敏感数据的安全性。这种保障不仅仅是对etcd存储的安全性保障,同样包括防止未授权用户对API服务器的访问,这是因为任何人都能通过创建pod并将Secret挂载来获得此类敏感数据。从Kubemetes1.7开始,etcd会以加密形式存储Secret,某种程度提高了系统的安全性。
Secret与ConfigMap的选择依据相对简单:
- 采用ConfigMap存储非敏感的文本配置数据。
- 采用Secret存储天生敏感的数据,通过键来引用。如果一个配置文件同时包含敏感与非敏感数据,该文件应该被存储在Secret中。
7.5.2 默认令牌 Secret 的介绍
对任意一个pod使用命令kubectl describe pod,输出往往包含以下信息:
每个pod都会被自动挂载上一个secret卷,这个卷引用的是图中的default-token-cfee9的secret。
( 因本人环境问题,输出不一样 )
(下面是我朋友虚拟机的输出,结果与示例匹配)
这个Secret包含三个条目——ca.crt、namespace、token,包含了从pod内部安全访问Kubernetes API Server所需的全部信息。
kubectl describe pod命令会显示secret卷被挂载的位置:
( 因环境问题,本人输出略有不同 )
注意:default-token Secret 默认会被挂载至每个容器。可以通过设置pod spec中的automountServiceAccountToken: false,或者设置pod使用的服务账户中的相同字段为false来关闭这种默认行为(后面会对服务账户进行讲解)。
通过kubectl exec观察被secret卷挂载的文件夹下包含三个文件:
7.5.3 创建Secret
实验:改进提供fortune服务的Nginx容器配置,使其能够服务于HTTPS流量。你需要创建私钥和证书,由于需要确保私服的安全性,可将其与证书同时存储Secret。
mkdir -p /root/k8s/fortune-https
cd /root/k8s/fortune-https
#首先,需要创建私钥和证书:
openssl genrsa -out https.key 2048
openssl req -new -x509 -key https.key -out https.cert -days 360 -subj /CN=www.kubia-example.com
#为了更好地理解Secret,额外创建一个内容为字符串的bar文件foo。
echo "bar" >foo
#用这3个文件创建一个Secret资源,名称为fortune-https
kubectl create secret generic fortune-https --from-file=./
7.5.4 对比 ConfigMap 与 Secret
Secret条目的内容会被Base64格式编码,而ConfigMap直接以纯文本展示。在处理YAML和JSON格式的Secret,需要在设置和读取相关条目时对内容进行编码、解码。
什么是Base64?
https://baike.baidu.com/item/base64/8545775?fr=aladdin
Base64是网络上最常见的用于传输8Bit字节码的编码方式之一。Base64就是一种基于64个可打印字符([A-Za-z0-9+/])来表示二进制数据的方法。Base64编码是从二进制到字符的过程。
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(38 = 46 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3。
示例: 转换前 10101101,10111010,01110110 转换后 00101011, 00011011 ,00101001 ,00110110 十进制 43 27 41 54 对应码表中的值 rbp2
为二进制数据创建Secret
采用Base64编码的原因很简单,Secret的条目可以涵盖二进制数据,而不仅仅是纯文本。Base64编码可以将二进制数据转换为纯文本以YAML或JSON格式展示。
提示:Secrets甚至可以被用来存储非敏感二进制数据。不过一个Secret的大小限于 1MB。
stringData字段介绍
Kubernetes允许通过Secret的stringData字段设置条目的纯文本值。
stringData字段是只写的(write-only),只能被用来设置条目值。通过kubctl get -o yaml 不会显示stringData字段。相反,stringData字段中的所有条目(比如上图的foo)会被Base64编码之后展示在data字段下。
在 pod 中读取Secret条目
通过secret卷或环境变量将Secret暴露给容器后,Secret条目的值会被解码并以真实形式(纯文本或二进制)写入对应的文件或环境变量。应用程序均无须主动解码,可直接读取文件内容或者查找并直接使用环境变量。
7.5.5 在 pod 中使用 Secret
修改fortune-config ConfigMap以开启 HTTPS
kubectl edit configmap fortune-config
-------------------------------------------------
data: #修改data字段,certs/是/etc/nginx的相对路径
my-nginx-config.conf: |
server {
listen 80;
listen 443 ssl;
server_name www.kubia-example.com;
ssl_certificate certs/https.cert;
ssl_certificate_key certs/https.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
sleep-interval: |
25
挂载fortune-https secret至pod
cd /root/k8s/
cat >fortune-pod-https.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: fortune-https
spec:
containers:
- image: 10.0.0.10:5000/luksa/fortune:env
name: html-generator
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: 10.0.0.10:5000/luksa/nginx
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: certs #证书和密钥的存放目录,需将secret卷挂载于此。
mountPath: /etc/nginx/certs/
readOnly: true
ports:
- containerPort: 80
- containerPort: 443
volumes:
- name: html
emptyDir: {}
- name: config
configMap:
name: fortune-config
items:
- key: my-nginx-config.conf
path: https.conf
- name: certs #引用fortune-https Secret来定义secret卷。
secret:
secretName: fortune-https
EOF
kubectl create -f fortune-pod-https.yaml
注意:与configMap卷相同,secret卷同样支持通过defaultModes属性指定卷中文件的默认权限。
测试 Nginx 是否正使用 Secret 中的证书与密钥
kubectl port-forward fortune-https 8443:443 &
curl https://localhost:8443 -k -v
Secret卷存储于内存
通过挂载secret卷至文件夹/etc/nginx/certs将证书与私钥成功传递给容器。secret卷使用tmpfs内存文件系统,存储在Secret中的数据不会写入磁盘,这样就无法被窃取。
注意:挂载后,sercret卷中的文件已经解码(因为不解码Nginx无法使用文件)。secret卷挂载后在目录中暴露的文件也使用了符号链接。
通过环境变量暴露Secret条目(不建议使用)
将Secret中的键foo暴露为环境变量FOO_SECRET
Kubernetes允许通过环境变量暴露Secret,然而此特性的使用往往不是一个好主意。应用程序通常会在错误报告时转储 (dump) 环境变量,或者是启动时打印在应用日志中,无意中暴露了它们(要知道,Secret条目中保存的是敏感数据)。另外,子进程会继承父进程的所有环境变量,如果是通过第三方二进制程序启动应用,你并不知道它使用敏感数据做了什么。
提示:由于敏感数据可能在无意中被暴露,通过环境变量暴露Secret给容器之前请再三思考。为了确保安全性,请始终采用secret卷的方式暴露Secret。
了解镜像拉取(image pull) Secrets
Kubernetes自身在有些时候希望我们能够传递证书给它,比如从某个私有镜像仓库拉取镜像时。这一点同样需通过Secrets来做到。大部分组织机构不希望它们的镜像开放给所有人,因此会使用私有镜像仓库。部署 pod 时,如果容器镜像位于私有仓库, Kubernetes 需拥有拉取镜像所需的证书。
在Docke Hub上使用私有镜像仓库
登录http://hub.docker.com ,找到对应的镜像仓库,勾选指定的复选框,将仓库标记为私有。
运行一个镜像来源于私有仓库的pod时,需要做以下两件事:
- 创建包含Docker镜像仓库证书的Secret。
- pod定义中的 imagePullSecrets 字段引用该Secret。
创建用于Docker 镜像仓库鉴权的 Secret
这是创建了一个docker-registry类型的mydockerhubsecret Secret,创建时需指定Docker Hub用户名、密码、邮箱。
通过kubectl describe观察新Secret内容时会发现有一个条目 .dockercfg,,相当于用户主目录下的 ~/.dockercfg 文件(本人环境是~/.docker/config.json文件)。该文件通常在运行 docker login 命令时由Docker自动创建。
在pod定义中使用docker-registry Secret
该pod定义中,字段 imagePullSecrets 引用了 mydockerhubsecret Secret 。
不需要为每个 pod 指定 镜像拉取 Secret
假设某系统中通常运行大量pod,你可能会好奇是否需要为每个pod都添加相同的镜像拉取Secret。幸运的是,情况并非如此。第12章中将会学习到如何通过添加Secret至ServiceAccount使所有pod都能自动添加上image pull Secret。
7.5.6 实验:镜像拉取Secrets的Docker本地仓库registry实现
#安装启动registry (registry服务器10.0.0.10)
mkdir -p /opt/registry
docker image pull registry
#修改配置文件 (registry服务器,客户端)
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://68rmyzg7.mirror.aliyuncs.com"],
"insecure-registries": ["10.0.0.10:5000"] #加这行
}
systemctl restart docker
#本地仓库加安全认证 (registry服务器)
yum install -y httpd-tools
mkdir -p /opt/registry-auth/
htpasswd -Bbn "user" "password" >>/opt/registry-auth/htpasswd
#启动带有密钥功能的registry容器 (registry服务器)
docker container run -d -p 5000:5000 -v /opt/registry-auth/:/auth/ -v /opt/registry:/var/lib/registry --name register-auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" registry
#push、pull镜像都需要进行login (registry服务器)
docker login 10.0.0.10:5000 #登录后密码记录在 ~/.docker/config.json
docker logout 10.0.0.10:5000
#创建用于Docker 镜像仓库鉴权的 Secret(注意docker-server要加端口,email随意)
kubectl create secret docker-registry myregistrysecret --docker-username=user --docker-password=password --docker-server=10.0.0.10:5000 --docker-email=my.email@163.com
#查看secret中的加密内容
kubectl get secrets myregistrysecret --output="jsonpath={.data.\.dockerconfigjson}" | base64 -d
#在pod定义中使用docker-registry Secret
cat >kubia-manual-with-private-image.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual
spec:
containers:
- image: 10.0.0.10:5000/luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
imagePullSecrets:
- name: myregistrysecret #引用 docker-registry Secret
EOF
kubectl create -f kubia-manual-with-private-image.yaml
注意:在创建pod后容器一直拉取镜像失败,提示no basic auth credentials。后来反复排查,确认是因为—docker-server没有指定本地仓库的端口 :5000。(也是因为我在指定image的时候加了端口)