# K8s 安全抽象:Secret
> [K8s Secret](https://kubernetes.io/zh/docs/concepts/configuration/secret/)
> [为 Pod 配置服务账户](https://kubernetes.io/zh/docs/tasks/configure-pod-container/configure-service-account/)
Secret 是对敏感信息的抽象,例如:密码、token、SSH key,其他对象可引用Secret。
Pod 使用 Secret 有两种场景:
* 作为 volume 中的文件被挂载到 Pod 中一个或多个容器中
* 拉取镜像时需要使用 secret 作为安全凭证
## Secret 分类
Secret 可分为三类:
* **docker-registry**: 创建一个给 Docker Registry 使用的 secret,实际是保存了账户密码。
* **generic**:从本地 file, directory 或者 literal value 创建一个 secret。
* **tls**:创建一个 TLS secret。
## 创建 generic secret
### 通过 literal(字面量)创建
创建一个order数据库的secret,包含账户密码两个字段。
```bash
kubectl create secret generic db-order-secret --from-literal=username=admin --from-literal=password=123456
```
> literal 中若包含特殊字符则需要进行转义,如"123456!" 需设置为 "123456\\!"
查看secret,`describe secret db-order-secret`,查看时隐藏了字段值,仅展示了字节数。
对应的API对象为 **Opaque([oʊˈpeɪk])**,译为"不透明物",等同于"敏感数据"。
```
Name: db-order-secret
Namespace: default
Labels:
Annotations:
Type: Opaque
Data
====
password: 6 bytes
username: 5 bytes
```
再看看其yaml形态:
```bash
kubectl get secret db-order-secret -o yaml
```
其中字段是base64编码后的值。
```yaml
apiVersion: v1
data:
password: MTIzNDU2
username: YWRtaW4=
kind: Secret
metadata:
creationTimestamp: "2020-03-14T04:21:29Z"
name: db-order-secret
namespace: default
resourceVersion: "28023453"
selfLink: /api/v1/namespaces/default/secrets/db-order-secret
uid: 5c60f906-ec8e-4277-8b96-caa75ef6589a
type: Opaque
```
解码password字段,值为`123456`。
```bash
$ echo MTIzNDU2 | base64 --decode
123456
```
### 通过文件创建
在当前目录创建一个RSA 公私钥对作为例子。
```bash
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): ./id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ./id_rsa.
Your public key has been saved in ./id_rsa.pub.
```
将私钥创建为secret,公钥可分发给另一方。
```bash
kubectl create secret generic rsa-key-secret --from-file=./id_rsa
```
### 通过yaml创建
通过yaml提供元数据以创建secret,字段需用base64先编码,如: `echo -n admin | base64`。
```yaml
apiVersion: v1
kind: Secret
metadata:
name: db-user-secret
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2
```
然后创建Secret即可。
```bash
kubectl apply -f ./secret.yaml
```
### Pod 中应用Secret
下面的例子演示了Secret 的三种应用方式。
* 作为volume直接挂载,在容器中可直接使用
* 在环境变量中使用secret
* 向特定目录映射secret
下面是Pod yaml。
```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx-container
image: nginx
env:
- name: SECRET_USERNAME
# 使用secret 做环境变量
valueFrom:
secretKeyRef:
name: db-user-secret
key: username
volumeMounts:
# 将secret作为volume挂载
- name: volume1
mountPath: "/etc/foo1"
readOnly: true
# 向特定目录映射secret
- name: volume2
mountPath: "/etc/foo2"
readOnly: true
volumes:
- name: volume1
secret:
secretName: db-user-secret
- name: volume2
secret:
secretName: db-user-secret
items:
- key: username
path: usernameFile
```
创建Pod,然后进入Pod验证效果。
* 做环境变量
```yaml
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: db-user-secret
key: username
```
结果如下:
```bash
$ env | grep SECRET_USERNAME
SECRET_USERNAME=admin
```
* 挂载到容器
```yaml
volumeMounts:
- name: volume1
mountPath: "/etc/foo1"
readOnly: true
```
效果如下:
```bash
$ ls -l /etc/foo1
total 0
lrwxrwxrwx 1 root root 15 Mar 14 07:49 password -> ..data/password
lrwxrwxrwx 1 root root 15 Mar 14 07:49 username -> ..data/username
```
* 向特定目录映射secret
```yaml
spec:
volumeMounts:
- name: volume2
mountPath: "/etc/foo2"
readOnly: true
volumes:
- name: volume2
secret:
secretName: db-user-secret
items:
- key: username
path: usernameFile
```
将 username 字段映射到了 `/etc/foo2/usernameFile` 中
```bash
$ cat /etc/foo2/usernameFile
admin
```
## 创建 docker-registry secret 拉取镜像
构建、部署时需要拉取镜像,可以向Pod提供一个secret专门用于拉取镜像。
以腾讯云镜像仓库为例,`ccr.ccs.tencentyun.com/easyk8s/nginx` 是一个私有的demo镜像。
利用下面yaml跑一个简单Pod:
```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx-container
image: ccr.ccs.tencentyun.com/easyk8s/nginx:1.17
```
运行后Pod Event如下,出现 `Error: ErrImagePull` 。
![](https://imgcdn.chenyongjun.vip/2020/03/14/2.png)
接下来创建一个secret专门用于拉取镜像。
```bash
kubectl create secret docker-registry registry-tecent-secret \
--docker-server=ccr.ccs.tencentyun.com \
--docker-username=name \
--docker-password=password
```
在 Pod 中使用 secret。
```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx-container
image: ccr.ccs.tencentyun.com/easyk8s/nginx:1.17
imagePullSecrets:
- name: registry-tecent-secret
```
应用yaml后Pod成功创建。
![](https://imgcdn.chenyongjun.vip/2020/03/14/3.png)
通过`kubectl get secret registry-tecent-secret -o yaml` 拿到的 `registry-tecent-secret` 的yaml结构如下:
```
apiVersion: v1
data:
.dockerconfigjson: eyJhdXR...
kind: Secret
metadata:
creationTimestamp: "2020-03-14T05:01:04Z"
name: registry-tecent-secret
namespace: default
resourceVersion: "28027921"
selfLink: /api/v1/namespaces/default/secrets/registry-tecent-secret
uid: a05ae3c0-b504-448b-bc78-8c540b2dda6c
type: kubernetes.io/dockerconfigjson
```
其中 **.dockerconfigjson** 通过base64解码后可以拿到docker-registry的账户密码。
## 创建tls secret
存储用于SSL通讯的私钥文件和证书文件,下面以nginx为例子。
预先准备好nginx ssl 通讯所需的key和crt文件.
```bash
kubectl create secret tls nginx-ssl-secret \
--key=nginx.key \
--cert=nginx.crt
```
下面是 secret 的数据,其类型为:`kubernetes.io/tls`
```bash
$ kubectl describe secret/nginx-ssl-secret
Name: nginx-ssl-secret
Namespace: default
Labels:
Annotations:
Type: kubernetes.io/tls
Data
====
tls.crt: 4241 bytes
tls.key: 1679 bytes
```
在nginx pod 中应用该secret,作为volume映射到容器中。
```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx-container
image: nginx
volumeMounts:
- name: nginx-crt
mountPath: /etc/nginx/ssl
readOnly: true
volumes:
- name: nginx-crt
secret:
secretName: nginx-ssl-secret
```
运行Pod后进入Pod查看 `/etc/nginx/ssl` 目录,可看到映射的证书、私钥文件,不过名字从 `nginx.crt, nginx.key` 变为了 `tls.crt, tls.key`。
```bash
$ ls -l /etc/nginx/ssl/
total 0
lrwxrwxrwx 1 root root 14 Mar 14 08:42 tls.crt -> ..data/tls.crt
lrwxrwxrwx 1 root root 14 Mar 14 08:42 tls.key -> ..data/tls.key
```
接着可以在nginx配置中使用这两个文件完成ssl通讯配置。
## 常用 Secret 示意图
![](https://imgcdn.chenyongjun.vip/2020/03/14/5.png)
扫码或搜索 codercyj 关注微信公众号, 结伴学习, 一起努力