Pod安全上下文
安全上下文(Security Context):K8s对Pod和容器提供的安全机制,可以设置Pod特权和访问控制。
安全上下文限制维度:
• 自主访问控制(Discretionary Access Control):基于用户ID(UID)和组ID(GID),来判定对对象(例文件)的访问权限。
• 安全性增强的 Linux(SELinux): 为对象赋予安全性标签。
• 以特权模式或者非特权模式运行。
• Linux Capabilities: 为进程赋予 root 用户的部分特权而非全部特权。
• AppArmor:定义Pod使用AppArmor限制容器对资源访问限制
• Seccomp:定义Pod使用Seccomp限制容器进程的系统调用
• AllowPrivilegeEscalation: 禁止容器中进程(通过 SetUID 或 SetGID 文件模式)获得特权提升。当容器以权模式运行或者具有CAP_SYS_ADMIN能力时,AllowPrivilegeEscalation总为True。
• readOnlyRootFilesystem:以只读方式加载容器的根文件系统。
案例1:设置容器以普通用户运行
背景:容器中的应用程序默认以root账号运行的,这个root与宿主机root账号是相同的,拥有大部分对Linux内核的系统调用权限,这样是不安全的,所以我们应该将容器以普通用户运行,减少应用程序对权限的使用。
可以通过两种方法设置普通用户:
• Dockerfile里使用USER指定运行用户
• K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID
Dockerfile方式:
flask-demo目录下:
Dockerfile
FROM python
RUN useradd python
RUN mkdir /data/www -p
COPY . /data/www
RUN chown -R python /data
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/ && \
pip install prometheus_client -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /data/www
USER python
CMD python main.py
main.py
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template("index.html")
if __name__ == "__main__":
app.run(host="0.0.0.0",port=8080)
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>Hello Python!</h1>
</body>
</html>
docker build -t flask-demo:v1 .
docker run -d --name=demo-v1 -p 8080:8080 flask-demo:v1
docker exec -it demo-v1 bash
id查看当前执行的账号
K8s里指定spec.securityContext.runAsUser方式
操作在node01上执行:
demo2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: demo2
name: demo2
spec:
replicas: 1
selector:
matchLabels:
app: demo2
template:
metadata:
labels:
app: demo2
spec:
nodeName: node01
securityContext:
runAsUser: 1000
containers:
- image: registry.cn-beijing.aliyuncs.com/howell-practice/kubernetes:v1
name: flask-demo
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
案例2:避免使用特权容器
背景:容器中有些应用程序可能需要访问宿主机设备、修改内核等需求,在默认情况下,容器没这个有这个能力,因此这时会考虑给容器设置特权模式。
containers:
- image: registry.cn-beijing.aliyuncs.com/howell-practice/kubernetes:v1
name: web
securityContext:
privileged: true
启用特权模式就意味着,你要为容器提供了访问Linux内核的所有能力,这是很危险的,为了减少系统调用的供给,可以使用Capabilities为容器赋予仅所需的能力。
实验:
pod-privileged.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-privileged
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
#securityContext:
#privileged: true
kubectl exec -it pod-privileged -- sh
测试挂载能力
mount -t tmpfs / /tmp
解除注释,新创建一个pod
pod02-privileged.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod02-privileged
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
privileged: true
进入容器,测试挂载能力
Linux Capabilities
Linux Capabilities:Capabilities 是一个内核级别的权限,它允许对内核调用权限进行更细粒度的控制,而不是简单地以 root 身份能力授权。Capabilities 包括更改文件权限、控制网络子系统和执行系统管理等功能。在securityContext 中,可以添加或删除 Capabilities,做到容器精细化权限控制。
查看node01当前的能力
capsh --print
查看容器中的能力
docker run -d centos sleep 24h
capsh --print
进入容器,执行capsh --print
示例1:容器默认没有挂载文件系统能力,添加SYS_ADMIN增加这个能力
cap-pod01.yaml
apiVersion: v1
kind: Pod
metadata:
name: cap-pod01
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
capabilities:
add: ["SYS_ADMIN"]
kubectl exec -it cap-pod01 -- sh
mount -t tmpfs / /tmp
案例2:只读挂载容器文件系统,防止恶意二进制文件创建
cap-pod02.yaml
apiVersion: v1
kind: Pod
metadata:
name: cap-pod02
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
readOnlyRootFilesystem: true
OPA Gatekeeper
OPA(Open Policy Agent):是一个开源的、通用策略引擎,可以将策略编写为代码。提供一个种高级声明性语言-Rego来编写策略,并把决策这一步骤从复杂的业务逻辑中解耦出来。
OPA可以用来做什么?
• 拒绝不符合条件的YAML部署
• 允许使用哪些仓库中的镜像
• 允许在哪个时间段访问系统
Gatekeeper 是基于OPA的一个Kubernetes 策略解决方案,可替代PSP或者部分RBAC功能。当在集群中部署了Gatekeeper组件,APIServer所有的创建、更新或者删除操作都会触发Gatekeeper来处理,如果不满足策略则拒绝。
部署Gatekeeper:
官网:
https://open-policy-agent.github.io/gatekeeper/website/docs/
安装文档:
https://open-policy-agent.github.io/gatekeeper/website/docs/install/
根据安装文档安装:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml
如果安装不了可以在网页下载
Gatekeeper的策略由两个资源对象组成:
Template:策略逻辑实现的地方,使用rego语言
Contsraint:负责Kubernetes资源对象的过滤或者为Template提供输入参数
案例1:禁止容器启用特权
模板:
通过匹配容器特权是否为true,判断是否拒绝
privileged_tpl.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: privileged
spec:
crd:
spec:
names:
kind: privileged
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package admission
violation[{"msg": msg}] { # 如果violation为true(表达式通过)说明违反约束
containers = input.review.object.spec.template.spec.containers
c_name := containers[0].name
containers[0].securityContext.privileged # 如果返回true,说明违反约束
msg := sprintf("提示:'%v'容器禁止启用特权!",[c_name])
}
# 查看资源
kubectl get ConstraintTemplate
约束:
制定约束的资源类型,为templete提供输入参数
privileged_constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: privileged
metadata:
name: privileged
spec:
match: # 匹配的资源
kinds:
- apiGroups: ["apps"]
kinds:
- "Deployment"
- "DaemonSet"
- "StatefulSet"
# 查看资源
kubectl get constraints
启用特权的测试容器
nginx_privileged.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy
name: nginx-deploy
spec:
replicas: 1
selector:
matchLabels:
app: nginx-deploy
template:
metadata:
creationTimestamp: null
labels:
app: nginx-deploy
spec:
containers:
- image: nginx
name: nginx
securityContext:
privileged: true
kubectl apply -f nginx_privileged.yaml
案例2:只允许使用特定的镜像仓库
模板:
image_tpl.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: image-check
spec:
crd:
spec:
names:
kind: image-check
validation:
openAPIV3Schema:
properties: # 需要满足条件的参数
prefix:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package image
violation[{"msg": msg}] {
containers = input.review.object.spec.template.spec.containers
image := containers[0].image
not startswith(image, input.parameters.prefix) # 镜像地址开头不匹配并取反则为true,说明违反约束
msg := sprintf("提示:'%v'镜像地址不在可信任仓库!", [image])
}
约束:
image_constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: image-check
metadata:
name: image-check
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds:
- "Deployment"
- "DaemonSet"
- "StatefulSet"
parameters: # 传递给opa的参数
prefix: "registry.cn-beijing.aliyuncs.com/"
测试:
kubectl create deployment nginx-deployment --image=nginx
Secret存储敏感数据
Secret是一个用于存储敏感数据的资源,所有的数据要经过base64编码,数据实际会存储在K8s中Etcd,然后通过创建Pod时引用该数据。
Pod使用secret数据有两种方式:
变量注入
数据卷挂载
kubectl create secret 支持三种数据类型:
• docker-registry:存储镜像仓库认证信息
• generic:从文件、目录或者字符串创建,例如存储用户名密码
• tls:存储证书,例如HTTPS证书
示例:将Mysql用户密码保存到Secret中存储
secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql
type: Opaque
data:
mysql-root-password: "MTIzNDU2"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: db
image: mysql:5.7.30
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql
key: mysql-root-password
验证下:
使用123456作为密码登入
安全沙箱运行容器
gVisor介绍
所知,容器的应用程序可以直接访问Linux内核的系统调用,容器在安全隔离上还是比较弱,虽然内核在不断地增强自身的安全特性,但由于内核自身代码极端复杂,CVE 漏洞层出不穷。所以要想减少这方面安全风险,就是做好安全隔离,阻断容器内程序对物理机内核的依赖。Google开源的一种gVisor容器沙箱技术就是采用这种思路,gVisor隔离容器内应用和内核之间访问,提供了大部分Linux内核的系统调用,巧妙的将容器内进程的系统调用转化为对gVisor的访问。
gVisor兼容OCI,与Docker和K8s无缝集成,很方便使用。
项目地址:https://github.com/google/gvisor
容器中应用程序直接与Linux内核交互
容器中应用程序需要通过gVisor才能使用资源
gVisor 由 3 个组件构成:
• Runsc 是一种 Runtime 引擎,负责容器的创建与销毁。
• Sentry 负责容器内程序的系统调用处理。
• Gofer 负责文件系统的操作代理,IO 请求都会由它转接到 Host 上。
gVisor与Docker集成
gVisor内核要求:Linux 3.17+
如果用的是CentOS7则需要升级内核,Ubuntu不需要。
CentOS7内核升级步骤:
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install kernel-ml-devel kernel-ml –y
grub2-set-default 0
reboot
uname -r
1、准备gVisor二进制文件
gvisor.zip
unzip gvisor.zip
sha512sum -c runsc.sha512
rm -f *.sha512
chmod a+x runsc
mv runsc /usr/local/bin
2、Docker配置使用gVisor
runsc install # 查看加的配置/etc/docker/daemon.json
systemctl restart docker
docker info | grep Runtime
使用runsc运行容器:
docker run -d --runtime=runsc nginx
进入容器查看可知,已经被gvisor内核接管
容器默认是使用宿主机的内核
使用dmesg验证:
docker run --runtime=runsc -it nginx dmesg
gVisor与Containerd集成
切换Containerd容器引擎
1、准备配置
cat > /etc/sysctl.d/99-kubernetes-cri.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl -system
2、安装
cd /etc/yum.repos.d
wget http://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo
yum install -y containerd.io
3、修改配置文件
pause镜像地址
• Cgroup驱动改为systemd
• 增加runsc容器运行时
• 配置docker镜像加速器
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
修改容器镜像:
vi /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.2"
修改驱动为systemd
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
增加runsc容器运行时
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
配置docker镜像加速器
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://b9pmyelo.mirror.aliyuncs.com"]
systemctl restart containerd
4、配置kubelet使用containerd
vi /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtimeendpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemd
systemctl restart kubelet
5、验证
kubectl get node -o wide
containerd也有 ctr 管理工具,但功能比较简单,一般使用crictl工具检查和调试容器。
项目地址:https://github.com/kubernetes-sigs/cri-tools/
准备crictl连接containerd配置文件:
cat > /etc/crictl.yaml << EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
EOF
下面是docker与crictl命令对照表:
K8s使用gVisor运行容器
RuntimeClass 是一个用于选择容器运行时配置的特性,容器运行时配置用于运行 Pod 中的容器。
创建RuntimeClass
RuntimeClass.yaml
apiVersion: node.k8s.io/v1 # RuntimeClass 定义于 node.k8s.io API 组
kind: RuntimeClass
metadata:
name: gvisor # 用来引用 RuntimeClass 的名字
handler: runsc # 对应的 CRI 配置的名称
创建Pod测试gVisor:
nginx-gvisor.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-gvisor
spec:
nodeName: node01
runtimeClassName: gvisor
containers:
- name: nginx
image: nginx
发布者:LJH,转发请注明出处:https://www.ljh.cool/34009.html