cks详细学习笔记
2024-04-09 11:11:23 1 举报
AI智能生成
CKS考试完全实操,需要动手!需要动手!需要动手!
作者其他创作
大纲/内容
3、k8s容器运行环境安全加固
系统模块
去除多余的系统模块
容器运行时会加载底层操作系统Linux内核中的模块,如果宿主机Linux的内核中加载了有漏洞的模块, 则也会影响到容器的安全,需要在操作系统层面进行加固。
在节点上加载一个存在漏洞的内核模块,然后观察pod中的变化,一旦宿主机被通过pcspkr模块工具,也会影响到容器
查看加载模块个数: lsmod | wc -l
加载模块:modprobe pcspkr
删除模块:modprobe -r pcspkr
查看加载模块个数: lsmod | wc -l
加载模块:modprobe pcspkr
删除模块:modprobe -r pcspkr
禁用多余模块
通过修改Linux操作系统的/etc/modprobe.d/xxxx.conf文件,决定Linux能够加载哪些模块。将存在风险的模块加入黑名单中,保证Linux系统和容器的安全
root@hh:~# cat /etc/modprobe.d/blacklist.conf
# This file lists those modules which we don't want to be loaded by
# alias expansion, usually so some other driver will be loaded for the
# device instead.
# evbug is a debug tool that should be loaded explicitly
blacklist evbug
...
通过修改Linux操作系统的/etc/modprobe.d/xxxx.conf文件,决定Linux能够加载哪些模块。将存在风险的模块加入黑名单中,保证Linux系统和容器的安全
root@hh:~# cat /etc/modprobe.d/blacklist.conf
# This file lists those modules which we don't want to be loaded by
# alias expansion, usually so some other driver will be loaded for the
# device instead.
# evbug is a debug tool that should be loaded explicitly
blacklist evbug
...
最小特权原则
最小特权原则 (Principle of least privilege,POLP) :是一种信息安全概念,即为用户提供执行其工作职责所需的最小权限等级或许可。
最小特权原则 (POLP) 重要性
• 减少网络攻击面:当今,大多数高级攻击都依赖于利用特权凭证。通过限制超级用户和管理员权限,最小权限执行有助于减少总体网络攻击面。
• 阻止恶意软件的传播: 通过在服务器或者在应用系统上执行最小权限,恶意软件攻击(例如SQL注入攻击)将很难提权来增加访问权限并横向移动破坏其他软件、设备。
• 有助于简化合规性和审核:许多内部政策和法规要求都要求组织对特权帐户实施最小权限原则,以防止对关键业务系统的恶意破坏。最小权限执行可以帮助组织证明对特权活动的完整审核跟踪的合规性。
• 阻止恶意软件的传播: 通过在服务器或者在应用系统上执行最小权限,恶意软件攻击(例如SQL注入攻击)将很难提权来增加访问权限并横向移动破坏其他软件、设备。
• 有助于简化合规性和审核:许多内部政策和法规要求都要求组织对特权帐户实施最小权限原则,以防止对关键业务系统的恶意破坏。最小权限执行可以帮助组织证明对特权活动的完整审核跟踪的合规性。
在团队中实施最小特权原则 (POLP)
• 在所有服务器、业务系统中,审核整个环境以查找特权帐户(例如SSH账号、管理后台账号、跳板机账号);
• 减少不必要的管理员权限,并确保所有用户和工具执行工作时所需的权限;
• 定期更改管理员账号密码;
• 监控管理员账号操作行为,告警通知异常活动。
将管理员帐户与标准帐户分开,并隔离特权用户会话。
将特权管理员帐户凭据提供给数字保管库,以开始保护和管理这些帐户。
启用即时访问特权升级,允许用户根据需要临时访问特权帐户或运行特权命令。
• 减少不必要的管理员权限,并确保所有用户和工具执行工作时所需的权限;
• 定期更改管理员账号密码;
• 监控管理员账号操作行为,告警通知异常活动。
将管理员帐户与标准帐户分开,并隔离特权用户会话。
将特权管理员帐户凭据提供给数字保管库,以开始保护和管理这些帐户。
启用即时访问特权升级,允许用户根据需要临时访问特权帐户或运行特权命令。
AppArmor限制容器对资源访问
AppArmor(Application Armor) 是一个 Linux 内核安全模块,可用于限制主机操作系统上运行的进程的功能。每个进程都可以拥有自己的安全配置文件。安全配置文件用来允许或禁止特定功能,例如网络访问、文件读/写/执行权限等。
Linux发行版内置:Ubuntu、Debian
Linux发行版内置:Ubuntu、Debian
Apparmor两种工作模式
• Enforcement(强制模式) :在这种模式下,配置文件里列出的限制条件都会得到执行,并且对于违反这些限制条件的程序会进行日志记录。
• Complain(投诉模式):在这种模式下,配置文件里的限制条件不会得到执行,Apparmor只是对程序的行为进行记录。一般用于调试。
• Complain(投诉模式):在这种模式下,配置文件里的限制条件不会得到执行,Apparmor只是对程序的行为进行记录。一般用于调试。
常用命令
• aa-enabled:查看apparmor是否开启
- apparmor_status:查看AppArmor配置文件的当前状态的
• apparmor_parser:将AppArmor配置文件加载到内核中
• aa-enforce:将AppArmor配置文件设置为强制模式,需要安装apparmor-utils软件包
- apparmor_status:查看AppArmor配置文件的当前状态的
• apparmor_parser:将AppArmor配置文件加载到内核中
- apparmor_parser <profile># 加载到内核中
- apparmor_parser -r <profile># 重新加载配置
- apparmor_parser -R <profile># 删除配置
• aa-enforce:将AppArmor配置文件设置为强制模式,需要安装apparmor-utils软件包
K8s使用AppArmor的先决条件
• K8s版本v1.4+,检查是否支持:kubectl describe node |grep AppArmor
• Linux内核已启用AppArmor,查看 cat /sys/module/apparmor/parameters/enabled
• 容器运行时需要支持AppArmor,目前Docker已支持
• Linux内核已启用AppArmor,查看 cat /sys/module/apparmor/parameters/enabled
• 容器运行时需要支持AppArmor,目前Docker已支持
在注解中指定AppArmor策略配置文件
apiVersion: v1
kind: Pod
metadata:
name: hello-apparmor
annotations:
container.apparmor.security.beta.kubernetes.io/<container_name>: localhost/<profile_ref>
...
• <container_name> Pod中容器名称
• <profile_ref> Pod所在宿主机上策略名,默认目录/etc/apparmor.d
kind: Pod
metadata:
name: hello-apparmor
annotations:
container.apparmor.security.beta.kubernetes.io/<container_name>: localhost/<profile_ref>
...
• <container_name> Pod中容器名称
• <profile_ref> Pod所在宿主机上策略名,默认目录/etc/apparmor.d
案例:容器文件系统访问限制
步骤:
1、将自定义策略配置文件保存到/etc/apparmor.d/
2、加载配置文件到内核:apparmor_parser <profile>
3、Pod注解指定策略配置名
1、将自定义策略配置文件保存到/etc/apparmor.d/
2、加载配置文件到内核:apparmor_parser <profile>
3、Pod注解指定策略配置名
工作流程
vi /etc/apparmor.d/k8s-apparmor-deny-write
#include <tunables/global>
profile k8s-apparmor-deny-write flags=(attach_disconnected) {
#include <abstractions/base>
file,
# Deny all file writes.
deny /** w,
}
• 第一行:导入依赖,遵循C语言约定
• 第二行:指定策略名
• 第三行:{} 策略块
#include <tunables/global>
profile k8s-apparmor-deny-write flags=(attach_disconnected) {
#include <abstractions/base>
file,
# Deny all file writes.
deny /** w,
}
• 第一行:导入依赖,遵循C语言约定
• 第二行:指定策略名
• 第三行:{} 策略块
访问文件权限模式
| 字符 | 描述 |
| ---- | ---- |
|r| 读|
w 写
a 追加
k 文件锁定
l 链接
x 可执行
| ---- | ---- |
|r| 读|
w 写
a 追加
k 文件锁定
l 链接
x 可执行
匹配目录和文件
|通配符|描述 |示例|
* | 在目录级别匹配零个或多个字符|
/dir/* 匹配目录中的任何文件
/dir/a* 匹配目录中以a开头的任意文件
/dir/*.png 匹配目录中以.png结尾的任意文件
/dir/a*/ 匹配/dir里面以a开头的目录
/dir/*a/ 匹配/dir里面以a结尾的目录
** | 在多个目录级别匹配零个或多个字符|
/dir/** 匹配/dir目录或者/dir目录下任何文件和目录
/dir/**/ 匹配/dir或者/dir下面任何目录
[]、[^] | 字符串,匹配其中任意字符 |
/dir/[^.]* 匹配/dir目录中以.之外的任何文件
/dir/**[^/] 匹配/dir目录或者/dir下面的任何目录中的任何文件
* | 在目录级别匹配零个或多个字符|
/dir/* 匹配目录中的任何文件
/dir/a* 匹配目录中以a开头的任意文件
/dir/*.png 匹配目录中以.png结尾的任意文件
/dir/a*/ 匹配/dir里面以a开头的目录
/dir/*a/ 匹配/dir里面以a结尾的目录
** | 在多个目录级别匹配零个或多个字符|
/dir/** 匹配/dir目录或者/dir目录下任何文件和目录
/dir/**/ 匹配/dir或者/dir下面任何目录
[]、[^] | 字符串,匹配其中任意字符 |
/dir/[^.]* 匹配/dir目录中以.之外的任何文件
/dir/**[^/] 匹配/dir目录或者/dir下面的任何目录中的任何文件
apiVersion: v1
kind: Pod
metadata:
name: hello-apparmor
annotations:
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-deny-write
spec:
containers:
- name: hello
image: busybox
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
kind: Pod
metadata:
name: hello-apparmor
annotations:
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-deny-write
spec:
containers:
- name: hello
image: busybox
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
进容器进行测试
kubectl exec hello-apparmor -- cat /proc/1/attr/current
k8s-apparmor-deny-write (enforce)
kubectl exec hello-apparmor -- touch test
touch: test: Permission denied
command terminated with exit code 1
kubectl exec hello-apparmor -- cat /proc/1/attr/current
k8s-apparmor-deny-write (enforce)
kubectl exec hello-apparmor -- touch test
touch: test: Permission denied
command terminated with exit code 1
Seccomp 限制容器进程系统调用
对于 Linux 来说,用户层一切资源相关操作都需要通过系统调用来完成;
系统调用实现技术层次上解耦,内核只关心系统调用API的实现,而不必关心谁调用的。
系统调用实现技术层次上解耦,内核只关心系统调用API的实现,而不必关心谁调用的。
调用关系图
seccomp是linux kernel从2.6.23版本开始所支持的一种安全机制。在Linux系统里,大量的系统调用(systemcall)直接暴露给用户态程序。
strace -fqc来查看命令背后会调用大量的syscall
strace -fqc来查看命令背后会调用大量的syscall
Seccomp(Secure computing mode) 是一个 Linux 内核安全模块,可用于应用进程允许使用的系统调用。
容器实际上是宿主机上运行的一个进程,共享宿主机内核,如果所有容器都具有任何系统调用的能力,那么容器如果被入侵,就很轻松绕过容器隔离更改宿主机系统权限或者进入宿主机。
这就可以使用Seccomp机制限制容器系统调用,有效减少攻击面。
Linux发行版内置:CentOS、Ubuntu
容器实际上是宿主机上运行的一个进程,共享宿主机内核,如果所有容器都具有任何系统调用的能力,那么容器如果被入侵,就很轻松绕过容器隔离更改宿主机系统权限或者进入宿主机。
这就可以使用Seccomp机制限制容器系统调用,有效减少攻击面。
Linux发行版内置:CentOS、Ubuntu
seccomp所包含的动作
SCMP_ACT_KILL:当进程进行对应的系统调用时,内核发出SIGSYS信号终止该进程,该进程不会接受到这个信号
SCMP_ACT_TRAP:当进程进行对应的系统调用时,该进程会接收到SIGSYS信号,并改变自身行为
SCMP_ACT_ERRNO:当进程进行对应的系统调用时,系统调用失败,进程会接收到errno返回值
SCMP_ACT_TRACE:当进程进行对应的系统调用时,进程会被跟踪
SCMP_ACT_ALLOW:允许进程进行对应的系统调用行为
SCMP_ACT_LOG:记录所有信息,/var/log/syslog
SCMP_ACT_TRAP:当进程进行对应的系统调用时,该进程会接收到SIGSYS信号,并改变自身行为
SCMP_ACT_ERRNO:当进程进行对应的系统调用时,系统调用失败,进程会接收到errno返回值
SCMP_ACT_TRACE:当进程进行对应的系统调用时,进程会被跟踪
SCMP_ACT_ALLOW:允许进程进行对应的系统调用行为
SCMP_ACT_LOG:记录所有信息,/var/log/syslog
Seccomp在Kubernetes 1.3版本引入,在1.19版本成为GA版本,因此K8s中使用Seccomp可以通过以下两种方式
1.19版本之前
annotations:
seccomp.security.alpha.kubernetes.io/pod: "localhost/<profile>"
或者我们设置到容器范围:
annotations:
container.security.alpha.kubernetes.io/<container-name>: "localhost/profile.json"
annotations:
seccomp.security.alpha.kubernetes.io/pod: "localhost/<profile>"
或者我们设置到容器范围:
annotations:
container.security.alpha.kubernetes.io/<container-name>: "localhost/profile.json"
1.19版本+
apiVersion: v1
kind: Pod
metadata:
name: hello-seccomp
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: <profile> # Pod所在宿主机上策略文件名,默认目录:/var/lib/kubelet/seccomp;默认目录的相对路径
containers:
...
apiVersion: v1
kind: Pod
metadata:
name: hello-seccomp
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: <profile> # Pod所在宿主机上策略文件名,默认目录:/var/lib/kubelet/seccomp;默认目录的相对路径
containers:
...
seccompProfile.type:
- Unconfined - 如果没有其他选择,Seccomp不会应用于容器进程(这是Kubernetes中的默认设置)。
- RuntimeDefault - 使用默认的容器运行时配置文件。
- Localhost - 指定自定有的profile文件。当type 为该值时,此时必须指定localhostProfile字段的值。
示例:禁止容器使用chmod
mkdir /var/lib/kubelet/seccomp
vi /var/lib/kubelet/seccomp/chmod.json
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": [
"chmod"
],
"action": "SCMP_ACT_ERRNO"
}
]
}
vi /var/lib/kubelet/seccomp/chmod.json
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": [
"chmod"
],
"action": "SCMP_ACT_ERRNO"
}
]
}
seccomp基本配置文件包括三个元素:
SCMP_ACT_ERRNO 阻止系统调用
- defaultAction:在syscalls部分未定义的任何系统调用默认动作为允许
- syscalls
SCMP_ACT_ERRNO 阻止系统调用
大多数容器运行时都提供一组允许或不允许的默认系统调用。通过使用 runtime/default 注释 或将Pod 或容器的安全上下文中的 seccomp 类型设置为 RuntimeDefault,可以轻松地在 Kubernetes中应用默认值。
Docker默认配置说明:https://docs.docker.com/engine/security/seccomp/
Docker默认配置说明:https://docs.docker.com/engine/security/seccomp/
ps -ef |grep dockerd
cat /etc/docker/seccomp.json
cat /etc/docker/seccomp.json
docker每个容器默认都设置了一个seccomp profile
docker会将seccomp传递给runc中的sepc.linux.seccomp。
可以通过—security-opt seccomp=xxx来设置docker的seccomp策略,xxx为json格式的文件,其中定义了seccomp规则。
也可以通过--security-opt seccomp=unconfined来关闭docker引入默认的seccomp规则的限制。
docker会将seccomp传递给runc中的sepc.linux.seccomp。
可以通过—security-opt seccomp=xxx来设置docker的seccomp策略,xxx为json格式的文件,其中定义了seccomp规则。
也可以通过--security-opt seccomp=unconfined来关闭docker引入默认的seccomp规则的限制。
分析容器系统调用:Sysdig
Sysdig:一个非常强大的系统监控、分析和故障排查工具。
汇聚 strace+tcpdump+htop+iftop+lsof 工具功能于一身!
sysdig 除了能获取系统资源利用率、进程、网络连接、系统调用等信息,
还具备了很强的分析能力,例如:
• 按照CPU使用率对进程排序
• 按照数据包对进程排序
• 打开最多的文件描述符进程
• 查看进程打开了哪些文件
• 查看进程的HTTP请求报文
• 查看机器上容器列表及资源使用情况
汇聚 strace+tcpdump+htop+iftop+lsof 工具功能于一身!
sysdig 除了能获取系统资源利用率、进程、网络连接、系统调用等信息,
还具备了很强的分析能力,例如:
• 按照CPU使用率对进程排序
• 按照数据包对进程排序
• 打开最多的文件描述符进程
• 查看进程打开了哪些文件
• 查看进程的HTTP请求报文
• 查看机器上容器列表及资源使用情况
sysdig 通过在内核的驱动模块注册系统调用的 hook,这样当有系统调用发生和完成的时候,它会把系统调用信息拷贝到特定的buffer,然后用户态组件对数据信息处理(解压、解析、过滤等),并最终通过 sysdig 命令行和用户进行交互。
sysdig
安装sysdig
rpm --import https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public
curl -s -o /etc/yum.repos.d/draios.repo https://s3.amazonaws.com/download.draios.com/stable/rpm/draios.repo
yum install epel-release -y
yum install sysdig -y
/usr/bin/sysdig-probe-loader # 加载驱动模块
curl -s -o /etc/yum.repos.d/draios.repo https://s3.amazonaws.com/download.draios.com/stable/rpm/draios.repo
yum install epel-release -y
yum install sysdig -y
/usr/bin/sysdig-probe-loader # 加载驱动模块
sysdig常用参数
• -l, --list:列出可用于过滤和输出的字段
• -M <num_seconds> :多少秒后停止收集
• -p <output_format>, --print=<output_format> :指定打印事件时使用的格式
• 使用-pc或-pcontainer 容器友好的格式
• 使用-pk或-pkubernetes k8s友好的格式
• -c <chiselname> <chiselargs>:指定内置工具,可直接完成具体的数据聚合、分析工作
• -w <filename>:保存到文件中
• -r <filename>:从文件中读取
• -M <num_seconds> :多少秒后停止收集
• -p <output_format>, --print=<output_format> :指定打印事件时使用的格式
• 使用-pc或-pcontainer 容器友好的格式
• 使用-pk或-pkubernetes k8s友好的格式
• -c <chiselname> <chiselargs>:指定内置工具,可直接完成具体的数据聚合、分析工作
• -w <filename>:保存到文件中
• -r <filename>:从文件中读取
sysdig输出格式
执行sysdig命令,实时输出大量系统调用。
示例:59509 23:59:19.023099531 0 kubelet (1738) < epoll_ctl
格式:%evt.num %evt.outputtime %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.info
• evt.num: 递增的事件号
• evt.time: 事件发生的时间
• evt.cpu: 事件被捕获时所在的 CPU,也就是系统调用是在哪个 CPU 执行的
• proc.name: 生成事件的进程名字
• thread.tid: 线程的 id,如果是单线程的程序,这也是进程的 pid
• evt.dir: 事件的方向(direction),> 代表进入事件,< 代表退出事件
• evt.type: 事件的名称,比如 open、stat等,一般是系统调用
• evt.args: 事件的参数。如果是系统调用,这些对应着系统调用的参数
自定义格式输出:sysdig -p "user:%user.name time:%evt.time proc_name:%proc.name"
示例:59509 23:59:19.023099531 0 kubelet (1738) < epoll_ctl
格式:%evt.num %evt.outputtime %evt.cpu %proc.name (%thread.tid) %evt.dir %evt.type %evt.info
• evt.num: 递增的事件号
• evt.time: 事件发生的时间
• evt.cpu: 事件被捕获时所在的 CPU,也就是系统调用是在哪个 CPU 执行的
• proc.name: 生成事件的进程名字
• thread.tid: 线程的 id,如果是单线程的程序,这也是进程的 pid
• evt.dir: 事件的方向(direction),> 代表进入事件,< 代表退出事件
• evt.type: 事件的名称,比如 open、stat等,一般是系统调用
• evt.args: 事件的参数。如果是系统调用,这些对应着系统调用的参数
自定义格式输出:sysdig -p "user:%user.name time:%evt.time proc_name:%proc.name"
sysdig过滤
• fd:根据文件描述符过滤,比如 fd 标号(fd.num)、fd 名字(fd.name)
• process:根据进程信息过滤,比如进程 id(proc.id)、进程名(proc.name)
• evt:根据事件信息过滤,比如事件编号、事件名
• user:根据用户信息过滤,比如用户 id、用户名、用户 home 目录
• syslog:根据系统日志过滤,比如日志的严重程度、日志的内容
• container:根据容器信息过滤,比如容器ID、容器名称、容器镜像
查看完整过滤器列表:sysdig -l
• process:根据进程信息过滤,比如进程 id(proc.id)、进程名(proc.name)
• evt:根据事件信息过滤,比如事件编号、事件名
• user:根据用户信息过滤,比如用户 id、用户名、用户 home 目录
• syslog:根据系统日志过滤,比如日志的严重程度、日志的内容
• container:根据容器信息过滤,比如容器ID、容器名称、容器镜像
查看完整过滤器列表:sysdig -l
示例
1、查看一个进程的系统调用
sysdig proc.name=kubelet
2、查看建立TCP连接的事件
sysdig evt.type=accept
3、查看/etc目录下打开的文件描述符
sysdig fd.name contains /etc
4、查看容器的系统调用
sysdig -M 10 container.name=web
注:还支持运算操作符,=、!=、>=、>、<、
<=、contains、in 、exists、and、or、not
sysdig proc.name=kubelet
2、查看建立TCP连接的事件
sysdig evt.type=accept
3、查看/etc目录下打开的文件描述符
sysdig fd.name contains /etc
4、查看容器的系统调用
sysdig -M 10 container.name=web
注:还支持运算操作符,=、!=、>=、>、<、
<=、contains、in 、exists、and、or、not
Chisels
Chisels:实用的工具箱,一组预定义的功能集合,用来分析特定的场景。
sysdig –cl 列出所有Chisels,以下是一些常用的:
• topprocs_cpu:输出按照 CPU 使用率排序的进程列表,例如sysdig -c
• topprocs_net:输出进程使用网络TOP
• topprocs_file:进程读写磁盘文件TOP
• topfiles_bytes:读写磁盘文件TOP
• netstat:列出网络的连接情况
sysdig –cl 列出所有Chisels,以下是一些常用的:
• topprocs_cpu:输出按照 CPU 使用率排序的进程列表,例如sysdig -c
• topprocs_net:输出进程使用网络TOP
• topprocs_file:进程读写磁盘文件TOP
• topfiles_bytes:读写磁盘文件TOP
• netstat:列出网络的连接情况
网络
# 查看使用网络的进程TOP
sysdig -c topprocs_net
# 查看建立连接的端口
sysdig -c fdcount_by fd.sport "evt.type=accept" -M 10
# 查看建立连接的端口
sysdig -c fdbytes_by fd.sport
# 查看建立连接的IP
sysdig -c fdcount_by fd.cip "evt.type=accept" -M 10
# 查看建立连接的IP
sysdig -c fdbytes_by fd.cip
硬盘
# 查看进程磁盘I/O读写
sysdig -c topprocs_file
# 查看进程打开的文件描述符数量
sysdig -c fdcount_by proc.name "fd.type=file" -M 10
# 查看读写磁盘文件
sysdig -c topfiles_bytes
sysdig -c topfiles_bytes proc.name=etcd
# 查看/tmp目录读写磁盘活动文件
sysdig -c fdbytes_by fd.filename "fd.directory=/tmp/"
CPU
# 查看CPU使用率TOP
sysdig -c topprocs_cpu
# 查看容器CPU使用率TOP
sysdig -pc -c topprocs_cpu container.name=web
sysdig -pc -c topprocs_cpu container.id=web
容器
# 查看机器上容器列表及资源使用情况
csysdig –vcontainers
# 查看容器资源使用TOP
sysdig -c topcontainers_cpu/topcontainers_net/topcontainers_file
# 查看使用网络的进程TOP
sysdig -c topprocs_net
# 查看建立连接的端口
sysdig -c fdcount_by fd.sport "evt.type=accept" -M 10
# 查看建立连接的端口
sysdig -c fdbytes_by fd.sport
# 查看建立连接的IP
sysdig -c fdcount_by fd.cip "evt.type=accept" -M 10
# 查看建立连接的IP
sysdig -c fdbytes_by fd.cip
硬盘
# 查看进程磁盘I/O读写
sysdig -c topprocs_file
# 查看进程打开的文件描述符数量
sysdig -c fdcount_by proc.name "fd.type=file" -M 10
# 查看读写磁盘文件
sysdig -c topfiles_bytes
sysdig -c topfiles_bytes proc.name=etcd
# 查看/tmp目录读写磁盘活动文件
sysdig -c fdbytes_by fd.filename "fd.directory=/tmp/"
CPU
# 查看CPU使用率TOP
sysdig -c topprocs_cpu
# 查看容器CPU使用率TOP
sysdig -pc -c topprocs_cpu container.name=web
sysdig -pc -c topprocs_cpu container.id=web
容器
# 查看机器上容器列表及资源使用情况
csysdig –vcontainers
# 查看容器资源使用TOP
sysdig -c topcontainers_cpu/topcontainers_net/topcontainers_file
4、最小化微服务漏洞
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:以只读方式加载容器的根文件系统。
• 安全性增强的 Linux(SELinux): 为对象赋予安全性标签。
• 以特权模式或者非特权模式运行。
• Linux Capabilities: 为进程赋予 root 用户的部分特权而非全部特权。
• AppArmor:定义Pod使用AppArmor限制容器对资源访问限制
• Seccomp:定义Pod使用Seccomp限制容器进程的系统调用
• AllowPrivilegeEscalation: 禁止容器中进程(通过 SetUID 或 SetGID 文件模式)获得特权提升。当容器以特权模式运行或者具有CAP_SYS_ADMIN能力时,AllowPrivilegeEscalation总为True。
• readOnlyRootFilesystem:以只读方式加载容器的根文件系统。
示例
设置容器以普通用户运行
容器中的应用程序默认以root账号运行的,这个root与宿主机root账号是相同的,拥有大部分对Linux内核的系统调用权限,这样是不安全的,所以我们应该将容器以普通用户运行,减少应用程序对权限的使用。
可以通过两种方法设置普通用户:
• Dockerfile里使用USER指定运行用户
• K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID
spec:
securityContext:
runAsUser: 1000 # 镜像里必须有这个用户UID
fsGroup: 1000 # 数据卷挂载后的目录属组设置为该组
containers:
- image: lizhenliang/flask-demo:root
name: web
securityContext:
allowPrivilegeEscalation: false # 不允许提权
可以通过两种方法设置普通用户:
• Dockerfile里使用USER指定运行用户
• K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID
spec:
securityContext:
runAsUser: 1000 # 镜像里必须有这个用户UID
fsGroup: 1000 # 数据卷挂载后的目录属组设置为该组
containers:
- image: lizhenliang/flask-demo:root
name: web
securityContext:
allowPrivilegeEscalation: false # 不允许提权
避免使用特权容器
容器中有些应用程序可能需要访问宿主机设备、修改内核等需求,在默认情况下,容器没这个有这个能力,因此这时会考虑给容器设置特权模式。
启用特权模式:
containers:
- image: lizhenliang/flask-demo:root
name: web
securityContext:
privileged: true
启用特权模式就意味着,你要为容器提供了访问Linux内核的所有能力,这是很危险的,为了减少系统调用的供给,可以使用Capabilities为容器赋予仅所需的能力。
启用特权模式:
containers:
- image: lizhenliang/flask-demo:root
name: web
securityContext:
privileged: true
启用特权模式就意味着,你要为容器提供了访问Linux内核的所有能力,这是很危险的,为了减少系统调用的供给,可以使用Capabilities为容器赋予仅所需的能力。
Linux Capabilities:
Capabilities 是一个内核级别的权限,它允许对内核调用权限进行更细粒度的控制,而不是简单地以 root 身份能力授权。
Capabilities 包括更改文件权限、控制网络子系统和执行系统管理等功能。在securityContext 中,可以添加或删除 Capabilities,做到容器精细化权限控制。
Capabilities 包括更改文件权限、控制网络子系统和执行系统管理等功能。在securityContext 中,可以添加或删除 Capabilities,做到容器精细化权限控制。
容器默认没有挂载文件系统能力,添加SYS_ADMIN增加这个能力
apiVersion: v1
kind: Pod
metadata:
name: cap-pod
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
capabilities:
add: ["SYS_ADMIN"]
apiVersion: v1
kind: Pod
metadata:
name: cap-pod
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
capabilities:
add: ["SYS_ADMIN"]
只读挂载容器文件系统,防止恶意二进制文件创建
apiVersion: v1
kind: Pod
metadata:
name: cap-pod
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
readOnlyRootFilesystem: true
apiVersion: v1
kind: Pod
metadata:
name: cap-pod
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
readOnlyRootFilesystem: true
Pod安全策略(PSP)
PodSecurityPolicy(简称PSP):Kubernetes中Pod部署时重要的安全校验手段,能够有效地约束应用运行时行为安全。
使用PSP对象定义一组Pod在运行时必须遵循的条件及相关字段的默认值,只有Pod满足这些条件才会被K8s接受。
使用PSP对象定义一组Pod在运行时必须遵循的条件及相关字段的默认值,只有Pod满足这些条件才会被K8s接受。
Pod 安全策略(Pod Security Policy)是集群级别的资源(默认不属于任何Namespace,但也可以在创建PSP规则时指定绑定到具体的Namespace),它能够控制Pod的各个安全方面。
Pod安全策略限制维度
|配置项 |描述|
privileged 启动特权容器。
hostPID,hostIPC 使用主机namespaces。
hostNetwork,hostPorts 使用主机网络和端口。
volumes 允许使用的挂载卷类型。
allowedHostPaths 允许hostPath类型挂载卷在主机上挂载的路径,通过pathPrefix字段声明允许挂载的主机路径前缀组。
allowedFlexVolumes 允许使用的指定FlexVolume驱动。
fsGroup 配置Pod中挂载卷使用的辅组ID。
readOnlyRootFilesystem 约束启动Pod使用只读的root文件系统。
runAsUser,runAsGroup,supplementalGroups 指定Pod中容器启动的用户ID以及主组和辅组ID。
allowPrivilegeEscalation,defaultAllowPrivilegeEscalation 约束Pod中是否允许配置allowPrivilegeEscalation=true,该配置会控制setuid的使用,同时控制程序是否可以使用额外的特权系统调用。
defaultAddCapabilities,requiredDropCapabilities,allowedCapabilities 控制Pod中使用的Linux Capabilities。
seLinux 控制Pod使用seLinux配置。
allowedProcMountTypes 控制Pod允许使用的ProcMountTypes。
annotations 配置Pod中容器使用的AppArmor或seccomp。
forbiddenSysctls,allowedUnsafeSysctls 控制Pod中容器使用的sysctl配置。
privileged 启动特权容器。
hostPID,hostIPC 使用主机namespaces。
hostNetwork,hostPorts 使用主机网络和端口。
volumes 允许使用的挂载卷类型。
allowedHostPaths 允许hostPath类型挂载卷在主机上挂载的路径,通过pathPrefix字段声明允许挂载的主机路径前缀组。
allowedFlexVolumes 允许使用的指定FlexVolume驱动。
fsGroup 配置Pod中挂载卷使用的辅组ID。
readOnlyRootFilesystem 约束启动Pod使用只读的root文件系统。
runAsUser,runAsGroup,supplementalGroups 指定Pod中容器启动的用户ID以及主组和辅组ID。
allowPrivilegeEscalation,defaultAllowPrivilegeEscalation 约束Pod中是否允许配置allowPrivilegeEscalation=true,该配置会控制setuid的使用,同时控制程序是否可以使用额外的特权系统调用。
defaultAddCapabilities,requiredDropCapabilities,allowedCapabilities 控制Pod中使用的Linux Capabilities。
seLinux 控制Pod使用seLinux配置。
allowedProcMountTypes 控制Pod允许使用的ProcMountTypes。
annotations 配置Pod中容器使用的AppArmor或seccomp。
forbiddenSysctls,allowedUnsafeSysctls 控制Pod中容器使用的sysctl配置。
PSP准入控制器
Pod安全策略实现为一个准入控制器,默认没有启用,当启用后会强制实施Pod安全策略,没有满足的Pod将无法创建。因此,建议在启用PSP之前先添加策略并对其授权。
启用Pod安全策略:
vi /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
...
systemctl restart kubelet
启用Pod安全策略:
vi /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
...
systemctl restart kubelet
PSP准入控制器工作流程
无论是通过user account还是service account(例如Deployment控制器会使用)创建pod,请求都会通过准入控制器。
如果我们在准入控制器处开启了PSP,并没有设置任何的规则,那么K8s账号(包括admin)发送的请求都会被拒绝。
当我们创建了PSP规则,那么K8s账号发送的请求必须在满足PSP的规则后才能够对Pod进行操作,否则请求依旧会被拒绝。
如果我们没有给自己创建的User Account或者Service Account赋予访问对应PSP的权限,用户发送的操作请求是无法到达PSP的,那么请求将被拒绝;由于默认的K8s admin的账号拥有着所有的权限,所以可以访问集群中所有的PSP。
如果我们在准入控制器处开启了PSP,并没有设置任何的规则,那么K8s账号(包括admin)发送的请求都会被拒绝。
当我们创建了PSP规则,那么K8s账号发送的请求必须在满足PSP的规则后才能够对Pod进行操作,否则请求依旧会被拒绝。
如果我们没有给自己创建的User Account或者Service Account赋予访问对应PSP的权限,用户发送的操作请求是无法到达PSP的,那么请求将被拒绝;由于默认的K8s admin的账号拥有着所有的权限,所以可以访问集群中所有的PSP。
开启PSP后创建pod的条件
1、用户有创建pod的授权
2、用户能访问PSP策略
3、PSP策略允许创建pod
2、用户能访问PSP策略
3、PSP策略允许创建pod
示例
禁止创建特权模式的Pod
# 创建psp策略,创建之后admin用户就可以创建pod了,但是不能创建特权pod
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: deny-privilege
spec:
privileged: false # 不允许特权Pod
# 下面是一些必要的字段
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: deny-privilege
spec:
privileged: false # 不允许特权Pod
# 下面是一些必要的字段
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
# 创建role1
kubectl create role role1 --verb=create,get,list,watch,delete --resource=pods
# 给用户hhf绑定role1,授权创建pod
kubectl create rolebinding rolebind1 --role=role1 --user=hhf
# 创建role2
kubectl create role role2 --verb=use --resource=podsecuritypolicy --resource-name=deny-privilege
# 给用户hhf绑定role2,准许创建pod
kubectl create rolebinding rolebind2 --role=role2 --user=hhf
kubectl create role role1 --verb=create,get,list,watch,delete --resource=pods
# 给用户hhf绑定role1,授权创建pod
kubectl create rolebinding rolebind1 --role=role1 --user=hhf
# 创建role2
kubectl create role role2 --verb=use --resource=podsecuritypolicy --resource-name=deny-privilege
# 给用户hhf绑定role2,准许创建pod
kubectl create rolebinding rolebind2 --role=role2 --user=hhf
hhf用户创建特权pod失败
# kubectl run test --image=nginx --kubeconfig=/root/cert/hhf-config --privileged=true
Error from server (Forbidden): pods "test" is forbidden: PodSecurityPolicy: unable to admit pod: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
# kubectl run test --image=nginx --kubeconfig=/root/cert/hhf-config --privileged=true
Error from server (Forbidden): pods "test" is forbidden: PodSecurityPolicy: unable to admit pod: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
使用Deployment部署pod
使用K8s的user调动Deployment控制器或者其他控制器部署pod时。而这些控制器在收到了我们的请求后,也会利用自己的Service Accounts完成后续操作。
如果Deployment控制器的SA没有被赋予访问运行Deployment操作的PSP规则的权限,就算我们使用admin这个默认的账号也无法完成Deployment的操作。
能创建deployment,但是pod没有部署
如果Deployment控制器的SA没有被赋予访问运行Deployment操作的PSP规则的权限,就算我们使用admin这个默认的账号也无法完成Deployment的操作。
能创建deployment,但是pod没有部署
# 创建clusterrole
kubectl create clusterrole psp-deny-privilege --verb=use --resource=podsecuritypolicies --resource-name=deny-privilege
# 给Deployment控制器的SA授权去访问PSP(pod依然没有部署,需要给replicaset控制器的SA授权)
kubectl create clusterrolebinding crolebinding-deny-privilege --clusterrole=psp-deny-privilege --serviceaccount=kube-system:deployment-controller
# 给replicaset控制器的SA授权去访问PSP
kubectl create clusterrolebinding crolebinding-deny-privilege2 --clusterrole=psp-deny-privilege --serviceaccount=kube-system:replicaset-controller
或者直接给Kube-system命名空间内所有的SA做绑定
kubectl create clusterrolebinding crolebinding-deny-privilege3 --clusterrole=psp-deny-privilege --serviceaccount=kube-system:replicaset-controller
kubectl create clusterrole psp-deny-privilege --verb=use --resource=podsecuritypolicies --resource-name=deny-privilege
# 给Deployment控制器的SA授权去访问PSP(pod依然没有部署,需要给replicaset控制器的SA授权)
kubectl create clusterrolebinding crolebinding-deny-privilege --clusterrole=psp-deny-privilege --serviceaccount=kube-system:deployment-controller
# 给replicaset控制器的SA授权去访问PSP
kubectl create clusterrolebinding crolebinding-deny-privilege2 --clusterrole=psp-deny-privilege --serviceaccount=kube-system:replicaset-controller
或者直接给Kube-system命名空间内所有的SA做绑定
kubectl create clusterrolebinding crolebinding-deny-privilege3 --clusterrole=psp-deny-privilege --serviceaccount=kube-system:replicaset-controller
禁止没指定普通用户运行的容器(runAsUser)
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: non-root
spec:
privileged: false # 不允许特权Pod
# 下面是一些必要的字段
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: MustRunAsNonRoot
fsGroup:
rule: RunAsAny
volumes:
- '*'
kind: PodSecurityPolicy
metadata:
name: non-root
spec:
privileged: false # 不允许特权Pod
# 下面是一些必要的字段
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: MustRunAsNonRoot
fsGroup:
rule: RunAsAny
volumes:
- '*'
OPA Gatekeeper
OPA概述
OPA(Open Policy Agent):是一个开源的、通用策略引擎,可以将策略编写为代码。提供一个种高级声明性语言-Rego来编写策略,并把决策这一步骤从复杂的业务逻辑中解耦出来。
OPA可以用来做什么?
• 拒绝不符合条件的YAML部署
• 允许使用哪些仓库中的镜像
• 允许在哪个时间段访问系统
• 等...
OPA可以用来做什么?
• 拒绝不符合条件的YAML部署
• 允许使用哪些仓库中的镜像
• 允许在哪个时间段访问系统
• 等...
OPA 为我们提供了 REST API,我们的系统可以调用这些 API 来检查请求负载是否满足策略。
任何需要做出授权或策略决策的服务都可以调用(策略查询),然后 OPA 可以根据策略的 Rego 代码做出决策,并将决策返回给相应地进一步处理请求的服务. 执行由实际服务本身完成,OPA 只负责做出决定。
任何需要做出授权或策略决策的服务都可以调用(策略查询),然后 OPA 可以根据策略的 Rego 代码做出决策,并将决策返回给相应地进一步处理请求的服务. 执行由实际服务本身完成,OPA 只负责做出决定。
在Docker中使用OPA
创建OPA空规则
mkdir -p /etc/docker/policies
vim /etc/docker/policies/authz.rego
package docker.authz
allow = true
mkdir -p /etc/docker/policies
vim /etc/docker/policies/authz.rego
package docker.authz
allow = true
安装OPA插件
docker plugin install openpolicyagent/opa-docker-authz-v2:0.8 opa-args="-policy-file /opa/policies/authz.rego"
安装插件并制定策略的位置。容器内的/opa/policies/authz.rego就等于宿主机的/etc/docker/policies/authz.rego,因为opa插件做了卷挂载。
docker plugin install openpolicyagent/opa-docker-authz-v2:0.8 opa-args="-policy-file /opa/policies/authz.rego"
安装插件并制定策略的位置。容器内的/opa/policies/authz.rego就等于宿主机的/etc/docker/policies/authz.rego,因为opa插件做了卷挂载。
配置 Docker 守护程序以使用插件进行授权
cat > /etc/docker/daemon.json <<EOF
{
"authorization-plugins": ["openpolicyagent/opa-docker-authz-v2:0.8"]
}
EOF
通知 Docker 守护进程重新加载配置文件:
kill -HUP $(pidof dockerd)
cat > /etc/docker/daemon.json <<EOF
{
"authorization-plugins": ["openpolicyagent/opa-docker-authz-v2:0.8"]
}
EOF
通知 Docker 守护进程重新加载配置文件:
kill -HUP $(pidof dockerd)
编写OPA规则,修改authz.rego文件
package docker.authz
default allow = false
allow {
not deny
}
deny {
seccomp_unconfined
}
seccomp_unconfined {
# This expression asserts that the string on the right-hand side is equal
# to an element in the array SecurityOpt referenced on the left-hand side.
input.Body.HostConfig.SecurityOpt[_] == "seccomp:unconfined"
}
default allow表示默认是拒绝所有;
deny {xxx} 表示拒绝xxx规则所定义的操作;
allow {not deny}则表示除了deny {xxx} 拒绝的,其他都允许
input.Body是固定的
HostConfig.SecurityOpt是docker inspect 查看到的
package docker.authz
default allow = false
allow {
not deny
}
deny {
seccomp_unconfined
}
seccomp_unconfined {
# This expression asserts that the string on the right-hand side is equal
# to an element in the array SecurityOpt referenced on the left-hand side.
input.Body.HostConfig.SecurityOpt[_] == "seccomp:unconfined"
}
default allow表示默认是拒绝所有;
deny {xxx} 表示拒绝xxx规则所定义的操作;
allow {not deny}则表示除了deny {xxx} 拒绝的,其他都允许
input.Body是固定的
HostConfig.SecurityOpt是docker inspect 查看到的
验证OPA规则是否生效
docker run -d --restart=always --name=web1 --security-opt seccomp:unconfined nginx
docker: Error response from daemon: authorization denied by plugin openpolicyagent/opa-docker-authz-v2:0.8: request rejected by administrative policy.
See 'docker run --help'.
docker run -d --restart=always --name=web1 --security-opt seccomp:unconfined nginx
docker: Error response from daemon: authorization denied by plugin openpolicyagent/opa-docker-authz-v2:0.8: request rejected by administrative policy.
See 'docker run --help'.
在K8s集群中使用OPA
Gatekeeper 是基于OPA的一个Kubernetes 策略解决方案,可替代PSP或者部分RBAC功能。
当在集群中部署了Gatekeeper组件,APIServer所有的创建、更新或者删除操作都会触发Gatekeeper来处理,如果不满足策略则拒绝。
当在集群中部署了Gatekeeper组件,APIServer所有的创建、更新或者删除操作都会触发Gatekeeper来处理,如果不满足策略则拒绝。
Gatekeeper 项目是 OPA 的 Kubernetes 特定实现。Gatekeeper 允许我们以 Kubernetes 原生方式使用 OPA 来执行所需的策略。
在 Kubernetes 集群上,Gatekeeper 作为ValidatingAdmissionWebhook安装。
在 Kubernetes 集群上,Gatekeeper 作为ValidatingAdmissionWebhook安装。
在请求通过 K8s API 服务器的身份验证和授权之后,但在它们持久化到数据库中之前,准入控制器可以拦截请求。如果任何准入控制器拒绝请求,则拒绝整个请求。准入控制器的局限性在于它们需要编译到 kube-apiserver 中,并且只有在 apiserver 启动时才能启用。
工作流程
部署Gatekeeper:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.7/deploy/gatekeeper.yaml
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/release-3.7/deploy/gatekeeper.yaml
Gatekeeper的策略由两个资源对象组成:
•Template:策略逻辑实现的地方,使用rego语言
•Contsraint:负责Kubernetes资源对象的过滤或者为Template提供输入参数
•Contsraint:负责Kubernetes资源对象的过滤或者为Template提供输入参数
Gatekeeper 使用 OPA 约束框架来描述和执行策略,在定义约束之前必须首先定义一个 ConstraintTemplate 对象,它描述了强制执行约束的 Rego 和约束的模式。约束的模式允许管理员对约束的行为进行微调,就像函数的参数一样。
示例
禁止xx.163.com开头的镜像
约束模板:
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: blacklistimages
spec:
crd:
spec:
names:
kind: BlacklistImages
targets:
- rego: |
package k8strustedimages
images {
image := input.review.object.spec.containers[_].image
not startswith(image, "xx.163.com/")
not startswith(image, "yy.163.com/")
}
violation[{"msg": msg}] {
not images
msg := "not trusted image!"
}
target: admission.k8s.gatekeeper.sh
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: blacklistimages
spec:
crd:
spec:
names:
kind: BlacklistImages
targets:
- rego: |
package k8strustedimages
images {
image := input.review.object.spec.containers[_].image
not startswith(image, "xx.163.com/")
not startswith(image, "yy.163.com/")
}
violation[{"msg": msg}] {
not images
msg := "not trusted image!"
}
target: admission.k8s.gatekeeper.sh
约束:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: BlacklistImages
metadata:
generation: 1
managedFields:
name: pod-trusted-images
resourceVersion: "14449"
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- Pod
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: BlacklistImages
metadata:
generation: 1
managedFields:
name: pod-trusted-images
resourceVersion: "14449"
spec:
match:
kinds:
- apiGroups:
- ""
kinds:
- Pod
验证OPA规则是否生效:
# 打tag
ctr i tag docker.io/library/busybox:latest xx.163.com/busybox:v1
# 尝试使用该镜像创建pod
kubectl run test --image=xx.163.com/busybox:v1
Error from server ([pod-trusted-images] not trusted image!): admission webhook "validation.gatekeeper.sh" denied the request: [pod-trusted-images] not trusted image!
# 打tag
ctr i tag docker.io/library/busybox:latest xx.163.com/busybox:v1
# 尝试使用该镜像创建pod
kubectl run test --image=xx.163.com/busybox:v1
Error from server ([pod-trusted-images] not trusted image!): admission webhook "validation.gatekeeper.sh" denied the request: [pod-trusted-images] not trusted image!
禁止容器启用特权
约束模板:
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
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
约束:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: privileged
metadata:
name: privileged
spec:
match: # 匹配的资源
kinds:
- apiGroups: ["apps"]
kinds:
- "Deployment"
- "DaemonSet"
- "StatefulSet“
# 查看资源
kubectl get constraints
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: privileged
metadata:
name: privileged
spec:
match: # 匹配的资源
kinds:
- apiGroups: ["apps"]
kinds:
- "Deployment"
- "DaemonSet"
- "StatefulSet“
# 查看资源
kubectl get constraints
只允许使用特定的镜像仓库
模板:
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])
}
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])
}
约束:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: image-check
metadata:
name: image-check
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds:
- "Deployment"
- "DaemonSet"
- "StatefulSet"
parameters: # 传递给opa的参数
prefix: "lizhenliang/
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: image-check
metadata:
name: image-check
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds:
- "Deployment"
- "DaemonSet"
- "StatefulSet"
parameters: # 传递给opa的参数
prefix: "lizhenliang/
namespace必须有gatekeeper标签
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("you must provide labels: %v", [missing])
}
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
# Schema for the `parameters` field
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg, "details": {"missing_labels": missing}}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("you must provide labels: %v", [missing])
}
命名空间必须要有gatekeeper标签
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-gk
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels: ["gatekeeper"]
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-gk
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels: ["gatekeeper"]
测试:
# kubectl create ns test2
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-gk] you must provide labels: {"gatekeeper"}
加上gk标签:
apiVersion: v1
kind: Namespace
metadata:
labels:
gatekeeper: test
name: test2
# kubectl create ns test2
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request: [ns-must-have-gk] you must provide labels: {"gatekeeper"}
加上gk标签:
apiVersion: v1
kind: Namespace
metadata:
labels:
gatekeeper: test
name: test2
Secret存储敏感数据
Secret是一个用于存储敏感数据的资源,所有的数据要经过base64编码,数据实际会存储在K8s中Etcd,
然后通过创建Pod时引用该数据。
然后通过创建Pod时引用该数据。
Pod使用secret数据有两种方式:
• 变量注入
• 数据卷挂载
• 数据卷挂载
kubectl create secret 支持三种数据类型:
• docker-registry:存储镜像仓库认证信息
• generic:从文件、目录或者字符串创建,例如存储用户名密码
• tls:存储证书,例如HTTPS证书
• generic:从文件、目录或者字符串创建,例如存储用户名密码
• tls:存储证书,例如HTTPS证书
示例
将Mysql用户密码保存到Secret中存储
[root@master ~]# echo -n 123 |base64
MTIz
apiVersion: v1
kind: Secret
metadata:
name: mysql
type: Opaque
data:
mysql-root-password: "MTIz"
---
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
kind: Secret
metadata:
name: mysql
type: Opaque
data:
mysql-root-password: "MTIz"
---
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
安全沙箱运行容器
gVisor
容器的应用程序可以直接访问Linux内核的系统调用,容器在安全隔离上还是比较弱,虽然内核在不断地增强自身的安全特性,但由于内核自身代码极端复杂,CVE 漏洞层出不穷。所以要想减少这方面安全风险,就是做好安全隔离,阻断容器内程序对物理机内核的依赖。
Google开源的一种gVisor容器沙箱技术就是采用这种思路,gVisor隔离容器内应用和内核之间访问,提供了大部分Linux内核的系统调用,巧妙的将容器内进程的系统调用转化为对gVisor的访问。gVisor兼容OCI,与Docker和K8s无缝集成,很方面使用。
项目地址:https://github.com/google/gvisor
Google开源的一种gVisor容器沙箱技术就是采用这种思路,gVisor隔离容器内应用和内核之间访问,提供了大部分Linux内核的系统调用,巧妙的将容器内进程的系统调用转化为对gVisor的访问。gVisor兼容OCI,与Docker和K8s无缝集成,很方面使用。
项目地址:https://github.com/google/gvisor
gVisor 拦截来自容器的系统调用,并通过用户空间内核运行它们, 用户空间内核采用 Go 编写,对底层主机的访问是受限的
不同类型的内核隔离
基于硬件虚拟化隔离
基于规则的安全隔离,seccomp、AppArmor 和 SELinux 等
gVisor的隔离机制
gVisor架构
gVisor 由 3 个组件构成:
• Runsc 是一种 Runtime 引擎,负责容器的创建与销毁。
• Sentry 负责容器内程序的系统调用处理。
• Gofer 负责文件系统的操作代理,IO 请求都会由它转接到 Host 上。
• Runsc 是一种 Runtime 引擎,负责容器的创建与销毁。
• Sentry 负责容器内程序的系统调用处理。
• Gofer 负责文件系统的操作代理,IO 请求都会由它转接到 Host 上。
gVisor与Docker集成
gVisor内核要求:Linux 3.17+
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二进制文件
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
使用runsc运行容器:
docker run -d --runtime=runsc nginx
使用dmesg验证:
docker run --runtime=runsc -it nginx dmesg
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二进制文件
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
使用runsc运行容器:
docker run -d --runtime=runsc nginx
使用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"
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://b9pmyelo.mirror.aliyuncs.com"]
...
systemctl restart containerd
4、配置kubelet使用containered
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
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"
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://b9pmyelo.mirror.aliyuncs.com"]
...
systemctl restart containerd
4、配置kubelet使用containered
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
准备crictl连接containerd配置文件:
cat > /etc/crictl.yaml << EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
EOF
cat > /etc/crictl.yaml << EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
EOF
RuntimeClass
RuntimeClass 是一个用于选择容器运行时配置的特性,容器运行时配置用于运行 Pod 中的容器。
创建RuntimeClass:
apiVersion: node.k8s.io/v1 # RuntimeClass 定义于 node.k8s.io API 组
kind: RuntimeClass
metadata:
name: gvisor # 用来引用 RuntimeClass 的名字
handler: runsc # 对应的 CRI 配置的名称
apiVersion: node.k8s.io/v1 # RuntimeClass 定义于 node.k8s.io API 组
kind: RuntimeClass
metadata:
name: gvisor # 用来引用 RuntimeClass 的名字
handler: runsc # 对应的 CRI 配置的名称
创建Pod测试gVisor:
apiVersion: v1
kind: Pod
metadata:
name: nginx-gvisor
spec:
runtimeClassName: gvisor
containers:
- name: nginx
image: nginx
kubectl get pod nginx-gvisor -o wide
kubectl exec nginx-gvisor -- dmesg
apiVersion: v1
kind: Pod
metadata:
name: nginx-gvisor
spec:
runtimeClassName: gvisor
containers:
- name: nginx
image: nginx
kubectl get pod nginx-gvisor -o wide
kubectl exec nginx-gvisor -- dmesg
6、监控、审计和运行时安全
监控容器运行时:Falco
Falco 是一个 Linux 安全工具,它使用系统调用来保护和监控系统。
Falco最初是由Sysdig开发的,后来加入CNCF孵化器,成为首个加入CNCF的运行时安全项目。
Falco提供了一组默认规则,可以监控内核态的异常行为,例如:
• 对于系统目录/etc, /usr/bin, /usr/sbin的读写行为
• 文件所有权、访问权限的变更
• 从容器打开shell会话
• 容器生成新进程
• 特权容器启动
Falco最初是由Sysdig开发的,后来加入CNCF孵化器,成为首个加入CNCF的运行时安全项目。
Falco提供了一组默认规则,可以监控内核态的异常行为,例如:
• 对于系统目录/etc, /usr/bin, /usr/sbin的读写行为
• 文件所有权、访问权限的变更
• 从容器打开shell会话
• 容器生成新进程
• 特权容器启动
Falco可以对Linux系统调用行为进行监控。
主要功能
- 从内核运行时采集Linux系统调用。
- 提供了一套强大的规则引擎,用于对Linux系统调用行为进行监控。
- 当系统调用违反规则时,会触发相应的告警。
Falco架构
架构:
- Falco内核模块持续采集Linxu内核态系统调用,并将采集到的系统调用事件通过ring buffer传递到用户态。
- Falco用户态模块解析系统调用事件,并经过配置的Falco规则处理识别出异常事件。
- 异常事件通过告警发送到标准输出、gRPC调用等。
- 用户态进程Userspace program:解析从driver接收到的时间,并发送告警。
- 配置信息:定义了Falco的运行配置,规则定义,告警配置等。
- Driver:
- 默认内核模块,基于libscap、libsinsp构建。
- BPF探针
- Userspace instrumentation
安装falco
rpm --import https://falco.org/repo/falcosecurity-3672BA8F.asc
curl -s -o /etc/yum.repos.d/falcosecurity.repo https://falco.org/repo/falcosecurity-rpm.repo
yum install epel-release -y
yum update
yum install falco -y
systemctl start falco
systemctl enable falco
curl -s -o /etc/yum.repos.d/falcosecurity.repo https://falco.org/repo/falcosecurity-rpm.repo
yum install epel-release -y
yum update
yum install falco -y
systemctl start falco
systemctl enable falco
配置文件
falco配置文件目录:/etc/falco
• falco.yaml falco配置与输出告警通知方式
• falco_rules.yaml 规则文件,默认已经定义很多威胁场景
• falco_rules.local.yaml 自定义扩展规则文件
• k8s_audit_rules.yaml K8s审计日志规则
• falco.yaml falco配置与输出告警通知方式
• falco_rules.yaml 规则文件,默认已经定义很多威胁场景
• falco_rules.local.yaml 自定义扩展规则文件
• k8s_audit_rules.yaml K8s审计日志规则
示例
告警规则示例(falco_rules.local.yaml):
- rule: The program "sudo" is run in a container
desc: An event will trigger every time you run sudo in a container
condition: evt.type = execve and evt.dir=< and container.id != host and proc.name = sudo
output: "Sudo run in container (user=%user.name %container.info parent=%proc.pname cmdline=%proc.cmdline)"
priority: ERROR
tags: [users, container]
参数说明:
• rule:规则名称,唯一
• desc:规则的描述
• condition: 条件表达式
• output:符合条件事件的输出格式
• priority:告警的优先级
• tags:本条规则的 tags 分类
- rule: The program "sudo" is run in a container
desc: An event will trigger every time you run sudo in a container
condition: evt.type = execve and evt.dir=< and container.id != host and proc.name = sudo
output: "Sudo run in container (user=%user.name %container.info parent=%proc.pname cmdline=%proc.cmdline)"
priority: ERROR
tags: [users, container]
参数说明:
• rule:规则名称,唯一
• desc:规则的描述
• condition: 条件表达式
• output:符合条件事件的输出格式
• priority:告警的优先级
• tags:本条规则的 tags 分类
威胁场景测试
1、监控系统二进制文件目录读写(默认规则)
2、监控根目录或者/root目录写入文件(默认规则)
3、监控运行交互式Shell的容器(默认规则)
4、监控容器创建的不可信任进程(自定义规则)
验证:tail -f /var/log/messages(告警通知默认输出到标准输出和系统日志)
2、监控根目录或者/root目录写入文件(默认规则)
3、监控运行交互式Shell的容器(默认规则)
4、监控容器创建的不可信任进程(自定义规则)
验证:tail -f /var/log/messages(告警通知默认输出到标准输出和系统日志)
创建规则
监控容器创建的不可信任进程规则,在falco_rules.local.yaml文件添加:
- rule: Unauthorized process on nginx containers
condition: spawned_process and container and container.image startswith nginx and not proc.name in (nginx)
desc: test
output: "Unauthorized process on nginx containers (user=%user.name container_name=%container.name
container_id=%container.id image=%container.image.repository shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)"
priority: WARNING
condition表达式解读:
• spawned_process 运行新进程
• container 容器
• container.image startswith nginx 以nginx开头的容器镜像
• not proc.name in (nginx) 不属于nginx的进程名称(允许进程名称列表)
重启falco应用新配置文件:
systemctl restart falco
- rule: Unauthorized process on nginx containers
condition: spawned_process and container and container.image startswith nginx and not proc.name in (nginx)
desc: test
output: "Unauthorized process on nginx containers (user=%user.name container_name=%container.name
container_id=%container.id image=%container.image.repository shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty)"
priority: WARNING
condition表达式解读:
• spawned_process 运行新进程
• container 容器
• container.image startswith nginx 以nginx开头的容器镜像
• not proc.name in (nginx) 不属于nginx的进程名称(允许进程名称列表)
重启falco应用新配置文件:
systemctl restart falco
Falco支持五种输出告警通知的方式
• 输出到标准输出(默认启用)
• 输出到文件
• 输出到Syslog(默认启用)
• 输出到HTTP服务
• 输出到其他程序(命令行管道方式)
告警配置文件:/etc/falco/falco.yaml
例如输出到指定文件:
file_output:
enabled: true
keep_alive: false
filename: /var/log/falco_events.log
• 输出到文件
• 输出到Syslog(默认启用)
• 输出到HTTP服务
• 输出到其他程序(命令行管道方式)
告警配置文件:/etc/falco/falco.yaml
例如输出到指定文件:
file_output:
enabled: true
keep_alive: false
filename: /var/log/falco_events.log
Falco告警集中化展示
• FalcoSideKick:一个集中收集并指定输出,支持大量方式输出,例如Influxdb、Elasticsearch等
项目地址 https://github.com/falcosecurity/falcosidekick
• FalcoSideKick-UI:告警通知集中图形展示系统
项目地址 https://github.com/falcosecurity/falcosidekick-ui
项目地址 https://github.com/falcosecurity/falcosidekick
• FalcoSideKick-UI:告警通知集中图形展示系统
项目地址 https://github.com/falcosecurity/falcosidekick-ui
部署Falco UI:
docker run -d \
-p 2801:2801 \
--name falcosidekick \
-e WEBUI_URL=http://192.168.31.71:2802 \
falcosecurity/falcosidekick
docker run -d \
-p 2802:2802 \
--name falcosidekick-ui \
falcosecurity/falcosidekick-ui
UI访问地址:http://192.168.31.71:2802/ui/
修改falco配置文件指定http方式输出:
json_output: true
json_include_output_property: true
http_output:
enabled: true
url: "http://192.168.31.71:2801/"
docker run -d \
-p 2801:2801 \
--name falcosidekick \
-e WEBUI_URL=http://192.168.31.71:2802 \
falcosecurity/falcosidekick
docker run -d \
-p 2802:2802 \
--name falcosidekick-ui \
falcosecurity/falcosidekick-ui
UI访问地址:http://192.168.31.71:2802/ui/
修改falco配置文件指定http方式输出:
json_output: true
json_include_output_property: true
http_output:
enabled: true
url: "http://192.168.31.71:2801/"
Kubernetes 审计日志
在Kubernetes集群中,API Server的审计日志记录了哪些用户、哪些服务请求操作集群资源,并且可以编写不同规则,控制忽略、存储的操作日志。
审计日志采用JSON格式输出,每条日志都包含丰富的元数据,例如请求的URL、HTTP方法、客户端来源等,你可以使用监控服务来分析API流量,以检测趋势或可能存在的安全隐患。
这些可能服务会访问API Server:
• 管理节点(controller-manager、scheduler)
• 工作节点(kubelet、kube-proxy)
• 集群服务(CoreDNS、Calico、HPA等)
• kubectl、API、Dashboard
审计日志采用JSON格式输出,每条日志都包含丰富的元数据,例如请求的URL、HTTP方法、客户端来源等,你可以使用监控服务来分析API流量,以检测趋势或可能存在的安全隐患。
这些可能服务会访问API Server:
• 管理节点(controller-manager、scheduler)
• 工作节点(kubelet、kube-proxy)
• 集群服务(CoreDNS、Calico、HPA等)
• kubectl、API、Dashboard
事件和阶段
当客户端向 API Server发出请求时,该请求将经历一个或多个阶段:
阶段 说明
RequestReceived 审核处理程序已收到请求
ResponseStarted 已发送响应标头,但尚未发送响应正文
ResponseComplete 响应正文已完成,不再发送任何字节
Panic 内部服务器出错,请求未完成
阶段 说明
RequestReceived 审核处理程序已收到请求
ResponseStarted 已发送响应标头,但尚未发送响应正文
ResponseComplete 响应正文已完成,不再发送任何字节
Panic 内部服务器出错,请求未完成
Kubernetes审核策略文件包含一系列规则,描述了记录日志的级别,
采集哪些日志,不采集哪些日志。
规则级别如下表所示:
级别 说明
None 不为事件创建日志条目
Metadata 创建日志条目。包括元数据,但不包括请求正文或响应正文
Request 创建日志条目。包括元数据和请求正文,但不包括响应正文
RequestResponse 创建日志条目。包括元数据、请求正文和响应正文
采集哪些日志,不采集哪些日志。
规则级别如下表所示:
级别 说明
None 不为事件创建日志条目
Metadata 创建日志条目。包括元数据,但不包括请求正文或响应正文
Request 创建日志条目。包括元数据和请求正文,但不包括响应正文
RequestResponse 创建日志条目。包括元数据、请求正文和响应正文
审计日志格式
审计日志支持写入本地文件和Webhook(发送到外部HTTP API)两种方式。
启用审计日志功能:
vi /etc/kubernetes/manifests/kube-apiserver.yaml
…
- --audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml
- --audit-log-path=/var/log/k8s_audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100
...
volumeMounts:
...
- mountPath: /etc/kubernetes/audit/audit-policy.yaml
name: audit
- mountPath: /var/log/k8s_audit.log
name: audit-log
volumes:
- name: audit
hostPath:
path: /etc/kubernetes/audit/audit-policy.yaml
type: File
- name: audit-log
hostPath:
path: /var/log/k8s_audit.log
type: FileOrCreate
注:需要使用hostpath数据卷将宿主机策略文件和日志文件挂载到容器中
启用审计日志功能:
vi /etc/kubernetes/manifests/kube-apiserver.yaml
…
- --audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml
- --audit-log-path=/var/log/k8s_audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100
...
volumeMounts:
...
- mountPath: /etc/kubernetes/audit/audit-policy.yaml
name: audit
- mountPath: /var/log/k8s_audit.log
name: audit-log
volumes:
- name: audit
hostPath:
path: /etc/kubernetes/audit/audit-policy.yaml
type: File
- name: audit-log
hostPath:
path: /var/log/k8s_audit.log
type: FileOrCreate
注:需要使用hostpath数据卷将宿主机策略文件和日志文件挂载到容器中
参数说明
audit-policy-file 审计日志策略文件
audit-log-path 审计日志输出文件
audit-log-maxage 审计日志保留的最大天数
audit-log-maxbackup 审计日志最大分片存储多少个日志文件
audit-log-maxsize 单个审计日志最大大小,单位MB
audit-policy-file 审计日志策略文件
audit-log-path 审计日志输出文件
audit-log-maxage 审计日志保留的最大天数
audit-log-maxbackup 审计日志最大分片存储多少个日志文件
audit-log-maxsize 单个审计日志最大大小,单位MB
示例
只记录指定资源操作日志
apiVersion: audit.k8s.io/v1
kind: Policy
# 忽略步骤,不为RequestReceived阶段生成审计日志
omitStages:
- "RequestReceived"
rules:
# 不记录日志
- level: None
users:
- system:apiserver
- system:kube-controller-manager
- system:kube-scheduler
- system:kube-proxy
- kubelet
# 针对资源记录日志
- level: Metadata
resources:
- group: ""
resources: ["pods"]
# - group: "apps"
# resources: ["deployments"]
# 其他资源不记录日志
- level: None
apiVersion: audit.k8s.io/v1
kind: Policy
# 忽略步骤,不为RequestReceived阶段生成审计日志
omitStages:
- "RequestReceived"
rules:
# 不记录日志
- level: None
users:
- system:apiserver
- system:kube-controller-manager
- system:kube-scheduler
- system:kube-proxy
- kubelet
# 针对资源记录日志
- level: Metadata
resources:
- group: ""
resources: ["pods"]
# - group: "apps"
# resources: ["deployments"]
# 其他资源不记录日志
- level: None
收集审计日志方案
• 审计日志文件+filebeat
• 审计webhook+logstash
• 审计webhook+falco
• 审计webhook+logstash
• 审计webhook+falco
1、集群部署与安全配置
安全机制
集群安全:TLS证书认证、RBAC
Security Context:限制容器的行为
Pod Security Policy:集群级的Pod安全策略,自动为集群内的 Pod 配置安全策略
Sysctls:允许容器设置内核参数
AppArmor:限制容器中应用对资源的访问权限
Network Policies:控制集群中网络通信
Seccomp:限制容器内进程的系统调用
CIS安全基准
互联网安全中心(CIS,Center for Internet Security),是一个非盈利组织,致力为互联网提供
免费的安全防御解决方案。
免费的安全防御解决方案。
CIS基准测试工具:kube-beach
介绍
Kube-bench是容器安全厂商Aquq推出的工具,以CIS K8s基准作为基础,来检查K8s是否安全部署。
主要查找不安全的配置参数、敏感的文件权限、不安全的帐户或公开端口等等。
主要查找不安全的配置参数、敏感的文件权限、不安全的帐户或公开端口等等。
部署
1、下载二进制包
https://github.com/aquasecurity/kube-bench/releases
https://github.com/aquasecurity/kube-bench/releases
2、解压使用
tar zxvf kube-bench_0.6.3_linux_amd64.tar.gz
mkdir /etc/kube-bench # 创建默认配置文件路径
mv cfg /etc/kube-bench/cfg
tar zxvf kube-bench_0.6.3_linux_amd64.tar.gz
mkdir /etc/kube-bench # 创建默认配置文件路径
mv cfg /etc/kube-bench/cfg
使用
常用参数:
• -s, --targets 指定要基础测试的目标,这个目标需要匹配cfg/<version>中的
文件名称,已有目标:master, controlplane, node, etcd, policies
• --version:指定k8s版本,如果未指定会自动检测
• --benchmark:手动指定CIS基准版本,不能与--version一起使用
• -s, --targets 指定要基础测试的目标,这个目标需要匹配cfg/<version>中的
文件名称,已有目标:master, controlplane, node, etcd, policies
• --version:指定k8s版本,如果未指定会自动检测
• --benchmark:手动指定CIS基准版本,不能与--version一起使用
示例:检查master组件安全配置
kube-bench run --targets=master
kube-bench run --targets=master
测试项目配置文件:/etc/kube-bench/cfg/cis-1.6/
• id:编号
• text:提示的文本
• audit:
• tests:测试项目
• remediation:修复方案
• scored:如果为true,kube-bench无法正常测试,
则会生成FAIL,如果为false,无法正常测试,则会
生成WARN。
• type:如果为manual则会生成WARN,如果为skip,
则会生成INFO
• id:编号
• text:提示的文本
• audit:
• tests:测试项目
• remediation:修复方案
• scored:如果为true,kube-bench无法正常测试,
则会生成FAIL,如果为false,无法正常测试,则会
生成WARN。
• type:如果为manual则会生成WARN,如果为skip,
则会生成INFO
Ingress配置证书
Ingress
K8s中的一个抽象资源,给管理员
提供一个暴露应用的入口定义方法
提供一个暴露应用的入口定义方法
Ingress Controller
根据Ingress生成具体
的路由规则,并对Pod负载均衡器
的路由规则,并对Pod负载均衡器
https
HTTPS是安全的HTTP,HTTP 协议中的内容都是明文传输,HTTPS 的目的是将这
些内容加密,确保信息传输安全。最后一个字母 S 指的是 SSL/TLS 协议,它位于
HTTP 协议与 TCP/IP 协议中间。
些内容加密,确保信息传输安全。最后一个字母 S 指的是 SSL/TLS 协议,它位于
HTTP 协议与 TCP/IP 协议中间。
HTTPS优势:
1、加密隐私数据:防止您访客的隐私信息(账号、地址、手机号等)被劫持或窃取。
2、安全身份认证:验证网站的真实性,防止钓鱼网站。
3、防止网页篡改:防止数据在传输过程中被篡改,保护用户体验。
4、地址栏安全锁:地址栏头部的“锁”型图标,提高用户信任度。
5、提高SEO排名:提高搜索排名顺序,为企业带来更多访问量。
1、加密隐私数据:防止您访客的隐私信息(账号、地址、手机号等)被劫持或窃取。
2、安全身份认证:验证网站的真实性,防止钓鱼网站。
3、防止网页篡改:防止数据在传输过程中被篡改,保护用户体验。
4、地址栏安全锁:地址栏头部的“锁”型图标,提高用户信任度。
5、提高SEO排名:提高搜索排名顺序,为企业带来更多访问量。
配置HTTPS步骤:
1、准备域名证书文件(来自:openssl/cfssl工具自签或者权威机构颁发)
2、将证书文件保存到Secret
kubectl create secret tls web-aliangedu-cn --
cert=web.aliangedu.cn.pem --key=web.aliangedu.cn-key.pem
3、Ingress规则配置tls
4、kubectl get ingress
5、测试,本地电脑绑定hosts记录对应ingress里面配置的域名,IP是
Ingress Concontroller Pod节点IP
1、准备域名证书文件(来自:openssl/cfssl工具自签或者权威机构颁发)
2、将证书文件保存到Secret
kubectl create secret tls web-aliangedu-cn --
cert=web.aliangedu.cn.pem --key=web.aliangedu.cn-key.pem
3、Ingress规则配置tls
4、kubectl get ingress
5、测试,本地电脑绑定hosts记录对应ingress里面配置的域名,IP是
Ingress Concontroller Pod节点IP
网络访问控制
默认情况下
Kubernetes 集群网络没任何网络限制,Pod 可以与任何其他 Pod 通信,在某些场景下就需要进行网络控制,
减少网络攻击面,提高安全性,这就会用到网络策略。
减少网络攻击面,提高安全性,这就会用到网络策略。
网络策略(Network Policy)
是一个K8s资源,用于限制Pod出入流量,提供Pod级别和Namespace级别网络访问控制。
采用的是白名单模式,符合规则的通过,不符合规则的拒绝。
网络策略的应用场景
• 应用程序间的访问控制,例如项目A不能访问项目B的Pod
• 开发环境命名空间不能访问测试环境命名空间Pod
• 当Pod暴露到外部时,需要做Pod白名单
• 多租户网络环境隔离
• 开发环境命名空间不能访问测试环境命名空间Pod
• 当Pod暴露到外部时,需要做Pod白名单
• 多租户网络环境隔离
网络策略
podSelector:目标Pod,根据标签选择。
policyTypes:策略类型,指定策略用于入站、出站流量。
Ingress:from是可以访问的白名单,可以来自于IP段、命名空间、Pod标签等,ports是可以访问的端口。
Egress:这个Pod组可以访问外部的IP段和端口。
policyTypes:策略类型,指定策略用于入站、出站流量。
Ingress:from是可以访问的白名单,可以来自于IP段、命名空间、Pod标签等,ports是可以访问的端口。
Egress:这个Pod组可以访问外部的IP段和端口。
三种设置策略的方式
ipBlock:匹配到的IP地址可以访问,except匹配到的无法访问 ;注意, 这些应该是集群外部 IP,因为 Pod IP存在时间短暂的且随机产生。例如,所作用的源IP则可能是LoadBalancer或Pod的node等。
podSelector:指定匹配到label的pod能够访问被保护的pod;
namespaceSelector:指定具体命名空间内的pod能够访问被保护的pod。
podSelector:指定匹配到label的pod能够访问被保护的pod;
namespaceSelector:指定具体命名空间内的pod能够访问被保护的pod。
网络策略工作流程:
1、创建Network Policy资源
2、Policy Controller监控网络策略,同步并通知节点上程序
3、节点上DaemonSet运行的程序从etcd中获取Policy,调用本地Iptables创建防火墙规则
2、Policy Controller监控网络策略,同步并通知节点上程序
3、节点上DaemonSet运行的程序从etcd中获取Policy,调用本地Iptables创建防火墙规则
案例
拒绝命名空间下所有Pod入、出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: test
spec:
podSelector: {} # 匹配本命名空间所有pod
policyTypes:
- Ingress
- Egress
# ingress和egress没有指定规则,则不允许任何流量进出pod
kind: NetworkPolicy
metadata:
name: deny-all
namespace: test
spec:
podSelector: {} # 匹配本命名空间所有pod
policyTypes:
- Ingress
- Egress
# ingress和egress没有指定规则,则不允许任何流量进出pod
拒绝其他命名空间Pod访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-namespaces
namespace: test
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # 匹配本命名空间所有pod
kind: NetworkPolicy
metadata:
name: deny-all-namespaces
namespace: test
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {} # 匹配本命名空间所有pod
允许其他命名空间Pod访问指定应用
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-namespaces
namespace: test
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: {} # 匹配所有命名空间的pod
kind: NetworkPolicy
metadata:
name: allow-all-namespaces
namespace: test
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: {} # 匹配所有命名空间的pod
同一个命名空间下应用之间限制访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-to-app
namespace: test
spec:
podSelector:
matchLabels:
run: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: client1
ports:
- protocol: TCP
port: 80
kind: NetworkPolicy
metadata:
name: app-to-app
namespace: test
spec:
podSelector:
matchLabels:
run: web
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: client1
ports:
- protocol: TCP
port: 80
只允许指定命名空间中的应用访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-web
namespace: dev
spec:
podSelector:
matchLabels:
env: dev
policyTypes:
- Ingress
ingress:
# 满足允许prod命名空间中的pod访问
- from:
- namespaceSelector:
matchLabels:
env: prod
# 允许pod标签为app=client1的pod访问,所有命名空间
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: client1
kind: NetworkPolicy
metadata:
name: dev-web
namespace: dev
spec:
podSelector:
matchLabels:
env: dev
policyTypes:
- Ingress
ingress:
# 满足允许prod命名空间中的pod访问
- from:
- namespaceSelector:
matchLabels:
env: prod
# 允许pod标签为app=client1的pod访问,所有命名空间
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: client1
2、集群强化
kubernetes安全框架
K8S安全控制框架主要由下面3个阶段进行控制,每一个阶段都支持
插件方式,通过API Server配置来启用插件。
1. Authentication(鉴权)
2. Authorization(授权)
3. Admission Control(准入控制)
插件方式,通过API Server配置来启用插件。
1. Authentication(鉴权)
2. Authorization(授权)
3. Admission Control(准入控制)
认证(Authentication)
K8s Apiserver提供三种客户端身份认证:
• HTTPS 证书认证:基于CA证书签名的数字证书认证(kubeconfig)
• HTTP Token认证:通过一个Token来识别用户(serviceaccount)
• HTTP Base认证:用户名+密码的方式认证(1.19版本弃用)
• HTTPS 证书认证:基于CA证书签名的数字证书认证(kubeconfig)
• HTTP Token认证:通过一个Token来识别用户(serviceaccount)
• HTTP Base认证:用户名+密码的方式认证(1.19版本弃用)
授权(Authorization)
RBAC(Role-Based Access Control,基于角色的访问控制):负责完成授权(Authorization)工作。
RBAC根据API请求属性,决定允许还是拒绝。
比较常见的授权维度:
• user:用户名
• group:用户分组
• 资源,例如pod、deployment
• 资源操作方法:get,list,create,update,patch,watch,delete
• 命名空间
• API组
RBAC根据API请求属性,决定允许还是拒绝。
比较常见的授权维度:
• user:用户名
• group:用户分组
• 资源,例如pod、deployment
• 资源操作方法:get,list,create,update,patch,watch,delete
• 命名空间
• API组
准入控制(Admission Control)
Adminssion Control实际上是一个准入控制器插件列表,发送到API Server的请求都需要经过这个列表中的每个准入控制器
插件的检查,检查不通过,则拒绝请求。
启用一个准入控制器:
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...
关闭一个准入控制器:
kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...
查看默认启用:
kubectl exec kube-apiserver-k8s-master -n kube-system -- kube-apiserver -h | grep enable-admission-plugins
插件的检查,检查不通过,则拒绝请求。
启用一个准入控制器:
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...
关闭一个准入控制器:
kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...
查看默认启用:
kubectl exec kube-apiserver-k8s-master -n kube-system -- kube-apiserver -h | grep enable-admission-plugins
基于角色的权限访问控制:RBAC
RBAC(Role-Based Access Control,基于角色的访问控制),
是K8s默认授权策略,并且是动态配置策略(修改即时生效)。
是K8s默认授权策略,并且是动态配置策略(修改即时生效)。
主体(subject)
• User:用户
• Group:用户组
• ServiceAccount:服务账号
角色
• Role:授权特定命名空间的访问权限
• ClusterRole:授权所有命名空间的访问权限
角色绑定
• RoleBinding:将角色绑定到主体(即subject)
• ClusterRoleBinding:将集群角色绑定到主体
注:RoleBinding在指定命名空间中执行授权,ClusterRoleBinding在集群范围执行授权。
• User:用户
• Group:用户组
• ServiceAccount:服务账号
角色
• Role:授权特定命名空间的访问权限
• ClusterRole:授权所有命名空间的访问权限
角色绑定
• RoleBinding:将角色绑定到主体(即subject)
• ClusterRoleBinding:将集群角色绑定到主体
注:RoleBinding在指定命名空间中执行授权,ClusterRoleBinding在集群范围执行授权。
k8s预定好了四个集群角色供用户使用,使用kubectl get
clusterrole查看,其中systemd:开头的为系统内部使用。
clusterrole查看,其中systemd:开头的为系统内部使用。
内置集群角色 描述
- cluster-admin 超级管理员,对集群所有权限
- admin 主要用于授权命名空间所有读写权限
- edit 允许对命名空间大多数对象读写操作,不允许查看或者修改角色、角色绑定。
- view 允许对命名空间大多数对象只读权限,不允许查看角色、角色绑定和Secret
案例
对用户授权访问K8s(TLS证书)
为指定用户授权访问default命名空间Pod读取权限
大致步骤:
1. 用K8S CA签发客户端证书
2. 生成kubeconfig授权文件
3. 创建RBAC权限策略
4. 指定kubeconfig文件测试权限:kubectl get pods --kubeconfig=./aliang.kubeconfig
大致步骤:
1. 用K8S CA签发客户端证书
2. 生成kubeconfig授权文件
3. 创建RBAC权限策略
4. 指定kubeconfig文件测试权限:kubectl get pods --kubeconfig=./aliang.kubeconfig
创建用户
1、为用户生成私钥,权限应为600
$ (umask 077; openssl genrsa -out $HOME/.certs/vagrant.key 2048)
2、创建证书签署请求,-subj参数的CN值为用户名,O的值为用户组
openssl req -new -key $HOME/.certs/vagrant.key -out $HOME/.certs/vagrant.csr -subj "/CN=vagrant/O=test"
3、用CA签署证书,设置时长
sudo openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -in $HOME/.certs/vagrant.csr -out $HOME/.certs/vagrant.crt
4、验证生成的数字证书信息(可选)
$ openssl x509 -in $HOME/.certs/vagrant.crt -text -noout
$ (umask 077; openssl genrsa -out $HOME/.certs/vagrant.key 2048)
2、创建证书签署请求,-subj参数的CN值为用户名,O的值为用户组
openssl req -new -key $HOME/.certs/vagrant.key -out $HOME/.certs/vagrant.csr -subj "/CN=vagrant/O=test"
3、用CA签署证书,设置时长
sudo openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -in $HOME/.certs/vagrant.csr -out $HOME/.certs/vagrant.crt
4、验证生成的数字证书信息(可选)
$ openssl x509 -in $HOME/.certs/vagrant.crt -text -noout
为用户生成kubeconfig配置文件
设置身份凭据,自动在vagrant家目录下生成.kube/config文件
$ kubectl config set-credentials vagrant --embed-certs=true --client-certificate=$HOME/.certs/vagrant.crt --client-key=$HOME/.certs/vagrant.key
设置上下文
$ kubectl config set-context vagrant@kubernetes --cluster=kubernetes --user=vagrant
设置集群信息
$ kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server="https://10.0.0.10:6443" --kubeconfig=$HOME/.kube/config
$ kubectl config set-credentials vagrant --embed-certs=true --client-certificate=$HOME/.certs/vagrant.crt --client-key=$HOME/.certs/vagrant.key
设置上下文
$ kubectl config set-context vagrant@kubernetes --cluster=kubernetes --user=vagrant
设置集群信息
$ kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server="https://10.0.0.10:6443" --kubeconfig=$HOME/.kube/config
角色权限分配
创建一个角色read-pods
# kubectl create role read-pods --verb=get,list,watch --resource=pods -n default --dry-run=client -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [“”] # api组,例如apps组,空值表示是核心API组,像namespace、pod、service、pv、pvc都在里面
resources: [“pods”] #资源名称(复数),例如pods、deployments、services
verbs: [“get”, “watch”, “list”] # 资源操作方法
# kubectl create role read-pods --verb=get,list,watch --resource=pods -n default --dry-run=client -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [“”] # api组,例如apps组,空值表示是核心API组,像namespace、pod、service、pv、pvc都在里面
resources: [“pods”] #资源名称(复数),例如pods、deployments、services
verbs: [“get”, “watch”, “list”] # 资源操作方法
将主体与角色绑定
给vagrant用户绑定read-pods角色
# kubectl create rolebinding read-pods -n default --role=read-pods --user=vagrant --dry-run=client -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User # 主体
name: vagrant # 主体名称
apiGroup: rbac.authorization.k8s.io
roleRef: # 绑定的角色
kind: Role
name: pod-reader # 角色名称
apiGroup: rbac.authorization.k8s.io
# kubectl create rolebinding read-pods -n default --role=read-pods --user=vagrant --dry-run=client -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User # 主体
name: vagrant # 主体名称
apiGroup: rbac.authorization.k8s.io
roleRef: # 绑定的角色
kind: Role
name: pod-reader # 角色名称
apiGroup: rbac.authorization.k8s.io
用户组:用户组的好处是无需单独为某个用户创建权限,统一为这个组名进
行授权,所有的用户都以组的身份访问资源。
行授权,所有的用户都以组的身份访问资源。
为test用户组统一授权:
# kubectl create rolebinding read-pods -n default --role=read-pods --group=test --dry-run=client -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: Group
name: dev
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
# kubectl create rolebinding read-pods -n default --role=read-pods --group=test --dry-run=client -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: Group
name: dev
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
对应用程序授权访问K8s(ServiceAccount)
先了解下ServiceAccount,简称SA,是一种用于让程序访问K8s API的服务账号。
• 当创建namespace时,会自动创建一个名为default的SA,这个SA没有绑定任何权限
• 当default SA创建时,会自动创建一个default-token-xxx的secret,并自动关联到SA
• 当创建Pod时,如果没有指定SA,会自动为pod以volume方式挂载这个default SA,在容器目录:/var/run/secrets/kubernetes.io/serviceaccount
验证默认SA权限:kubectl --as=system:serviceaccount:default:default get pods
• 当创建namespace时,会自动创建一个名为default的SA,这个SA没有绑定任何权限
• 当default SA创建时,会自动创建一个default-token-xxx的secret,并自动关联到SA
• 当创建Pod时,如果没有指定SA,会自动为pod以volume方式挂载这个default SA,在容器目录:/var/run/secrets/kubernetes.io/serviceaccount
验证默认SA权限:kubectl --as=system:serviceaccount:default:default get pods
需求:授权容器中Python程序对K8s API访问权限
实施大致步骤:
1. 创建Role
2. 创建ServiceAccount
3. 将ServiceAccount与Role绑定
4. 为Pod指定自定义的SA
5. 进入容器里执行Python程序测试操作K8s API权限
实施大致步骤:
1. 创建Role
2. 创建ServiceAccount
3. 将ServiceAccount与Role绑定
4. 为Pod指定自定义的SA
5. 进入容器里执行Python程序测试操作K8s API权限
命令行使用:授权SA只能查看test命名空间控制器的权限
# 创建角色
kubectl create role role-test --verb=get,list \
--resource=deployments,daemonsets,statefulsets -n test
# 创建服务账号
kubectl create serviceaccount app-demo -n test
# 将服务账号绑定角色
kubectl create rolebinding role-test:app-demo \
--serviceaccount=test:app-demo --role=role-test -n test
# 测试
kubectl --as=system:serviceaccount:test:app-demo \
get pods -n test
kubectl --as=system:serviceaccount:test:app-demo get deployment -n test
# 创建角色
kubectl create role role-test --verb=get,list \
--resource=deployments,daemonsets,statefulsets -n test
# 创建服务账号
kubectl create serviceaccount app-demo -n test
# 将服务账号绑定角色
kubectl create rolebinding role-test:app-demo \
--serviceaccount=test:app-demo --role=role-test -n test
# 测试
kubectl --as=system:serviceaccount:test:app-demo \
get pods -n test
kubectl --as=system:serviceaccount:test:app-demo get deployment -n test
yaml文件
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-demo
namespace: test
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-test
namespace: test
rules:
- apiGroups: ["apps"]
resources: ["deployments","daemonsets","statefulsets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-test:app-demo
namespace: test
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-test
subjects:
- kind: ServiceAccount
name: app-demo
namespace: test
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-demo
namespace: test
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-test
namespace: test
rules:
- apiGroups: ["apps"]
resources: ["deployments","daemonsets","statefulsets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-test:app-demo
namespace: test
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: role-test
subjects:
- kind: ServiceAccount
name: app-demo
namespace: test
资源配额 ResourceQuota
当多个团队、多个用户共享使用K8s集群时,会出现不均匀资源使用,默认情况下先到先得,这时可以通过
ResourceQuota来对命名空间资源使用总量做限制,从而解决这个问题。
使用流程:k8s管理员为每个命名空间创建一个或多个ResourceQuota对象,定义资源使用总量,K8s会跟踪命名空间
资源使用情况,当超过定义的资源配额会返回拒绝。
ResourceQuota来对命名空间资源使用总量做限制,从而解决这个问题。
使用流程:k8s管理员为每个命名空间创建一个或多个ResourceQuota对象,定义资源使用总量,K8s会跟踪命名空间
资源使用情况,当超过定义的资源配额会返回拒绝。
ResourceQuota功能是一个准入控制插件,默认已经启用。
支持的资源 描述
limits.cpu/memory 所有Pod上限资源配置总量不超过该值(所有非终止状态的Pod)
requests.cpu/memory 所有Pod请求资源配置总量不超过该值(所有非终止状态的Pod)
cpu/memory 等同于requests.cpu/requests.memory
requests.storage 所有PVC请求容量总和不超过该值
persistentvolumeclaims 所有PVC数量总和不超过该值
<storage-class-name>.storageclass.storage.k8s.io/requests.storage 所有与<storage-class-name>相关的PVC请求容量总和不超过该值
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims 所有与<storage-class-name>相关的PVC数量总和不超过该值
pods、count/deployments.apps、count/statfulsets.apps、count/services(services.loadbalancers、services.nodeports)、count/secrts、count/configmaps、count/job.batch、count/cronjobs.batch 创建资源数量不超过该值
limits.cpu/memory 所有Pod上限资源配置总量不超过该值(所有非终止状态的Pod)
requests.cpu/memory 所有Pod请求资源配置总量不超过该值(所有非终止状态的Pod)
cpu/memory 等同于requests.cpu/requests.memory
requests.storage 所有PVC请求容量总和不超过该值
persistentvolumeclaims 所有PVC数量总和不超过该值
<storage-class-name>.storageclass.storage.k8s.io/requests.storage 所有与<storage-class-name>相关的PVC请求容量总和不超过该值
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims 所有与<storage-class-name>相关的PVC数量总和不超过该值
pods、count/deployments.apps、count/statfulsets.apps、count/services(services.loadbalancers、services.nodeports)、count/secrts、count/configmaps、count/job.batch、count/cronjobs.batch 创建资源数量不超过该值
示例
对象数量配额
apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
namespace: test
spec:
hard:
pods: "10"
count/deployments.apps: "3"
count/services: "3"
kind: ResourceQuota
metadata:
name: object-counts
namespace: test
spec:
hard:
pods: "10"
count/deployments.apps: "3"
count/services: "3"
计算资源配额
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-resources
namespace: test
spec:
hard:
requests.cpu: "4"
requests.memory: 10Gi
limits.cpu: "6"
limits.memory: 12Gi
kind: ResourceQuota
metadata:
name: compute-resources
namespace: test
spec:
hard:
requests.cpu: "4"
requests.memory: 10Gi
limits.cpu: "6"
limits.memory: 12Gi
存储资源配额
apiVersion: v1
kind: ResourceQuota
metadata:
name: storage-resources
namespace: test
spec:
hard:
requests.storage: "10G"
managed-nfsstorage.storageclass.storage.k8s.io/requests.storage: "5G"
kind: ResourceQuota
metadata:
name: storage-resources
namespace: test
spec:
hard:
requests.storage: "10G"
managed-nfsstorage.storageclass.storage.k8s.io/requests.storage: "5G"
查看配额
kubectl get quota -n test
资源限制 LimitRange
默认情况下,K8s集群上的容器对计算资源没有任何限制,可能会导致个别容器资源过大导致影响其他容器正常工
作,这时可以使用LimitRange定义Pod和容器默认CPU和内存请求值或者最大上限。
LimitRange限制维度:
• 限制容器配置requests.cpu/memory,limits.cpu/memory的最小、最大值
• 限制容器配置requests.cpu/memory,limits.cpu/memory的默认值
• 限制PVC配置requests.storage的最小、最大值
作,这时可以使用LimitRange定义Pod和容器默认CPU和内存请求值或者最大上限。
LimitRange限制维度:
• 限制容器配置requests.cpu/memory,limits.cpu/memory的最小、最大值
• 限制容器配置requests.cpu/memory,limits.cpu/memory的默认值
• 限制PVC配置requests.storage的最小、最大值
示例
计算资源最大、最小限制
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-memory-min-max
namespace: test
spec:
limits:
- max: # 容器能设置limit的最大值
cpu: 1
memory: 1Gi
min: # 容器能设置request的最小值
cpu: 200m
memory: 200Mi
type: Container
kind: LimitRange
metadata:
name: cpu-memory-min-max
namespace: test
spec:
limits:
- max: # 容器能设置limit的最大值
cpu: 1
memory: 1Gi
min: # 容器能设置request的最小值
cpu: 200m
memory: 200Mi
type: Container
计算资源默认值限制
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-memory-min-max
namespace: test
spec:
limits:
- default:
cpu: 500m
memory: 500Mi
defaultRequest:
cpu: 300m
memory: 300Mi
type: Container
kind: LimitRange
metadata:
name: cpu-memory-min-max
namespace: test
spec:
limits:
- default:
cpu: 500m
memory: 500Mi
defaultRequest:
cpu: 300m
memory: 300Mi
type: Container
存储资源最大、最小限制
apiVersion: v1
kind: LimitRange
metadata:
name: storage-min-max
namespace: test
spec:
limits:
- type: PersistentVolumeClaim
max:
storage: 10Gi
min:
storage: 1Gi
kind: LimitRange
metadata:
name: storage-min-max
namespace: test
spec:
limits:
- type: PersistentVolumeClaim
max:
storage: 10Gi
min:
storage: 1Gi
查看限制
kubectl get limits -n test
kubectl describe limits -n test
kubectl describe limits -n test
5、供应链安全
可信任软件供应链
可信任软件供应链:指在建设基础架构过程中,涉及的软件都是可信任的。
在K8s领域可信软件供应链主要是指镜像,因为一些软件交付物都是镜像,部署的最小载体。
构建镜像Dockerfile文件优化
• 减少镜像层:一次RUN指令形成新的一层,尽量Shell命令都写在一行,减少镜像层。
• 清理无用文件:清理对应的残留数据,例如yum缓存。
• 清理无用的软件包:基础镜像默认会带一些debug工具,可以删除掉,仅保留应用程序所需软件,防止黑客利用。
• 选择最小的基础镜像:例如alpine
• 使用非root用户运行:USER指令指定普通用户
• 清理无用文件:清理对应的残留数据,例如yum缓存。
• 清理无用的软件包:基础镜像默认会带一些debug工具,可以删除掉,仅保留应用程序所需软件,防止黑客利用。
• 选择最小的基础镜像:例如alpine
• 使用非root用户运行:USER指令指定普通用户
示例
构建python web镜像
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/
WORKDIR /data/www
USER python
CMD python main.py
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/
WORKDIR /data/www
USER python
CMD python main.py
镜像漏洞扫描工具:Trivy
Trivy:是一种用于容器镜像、文件系统、Git仓库的漏洞扫描工具。发现目标软件存在的漏洞。
Trivy易于使用,只需安装二进制文件即可进行扫描,方便集成CI系统。
Trivy易于使用,只需安装二进制文件即可进行扫描,方便集成CI系统。
示例
# 容器镜像扫描
trivy image nginx
trivy image -i nginx.tar
# 打印指定(高危、严重)漏洞信息
trivy image -s HIGH nginx
trivy image -s HIGH, CRITICAL nginx
# JSON格式输出并保存到文件
trivy image nginx -f json -o /root/output.json
trivy image nginx
trivy image -i nginx.tar
# 打印指定(高危、严重)漏洞信息
trivy image -s HIGH nginx
trivy image -s HIGH, CRITICAL nginx
# JSON格式输出并保存到文件
trivy image nginx -f json -o /root/output.json
检查YAML文件安全配置:kubesec
kubesec:是一个针对K8s资源清单文件进行安全配置评估的工具,根据安全配置最佳实践来验证并给出建议。
示例:
kubesec scan deployment.yaml
或者使用容器环境执行检查
docker run -i kubesec/kubesec scan /dev/stdin < deployment.yaml
kubesec内置一个HTTP服务器,可以直接启用,远程调用。
• 二进制
kubesec http 8080 &
• Docker容器
docker run -d -p 8080:8080 kubesec/kubesec http 8080
示例:
curl -sSX POST --data-binary @deployment.yaml http://192.168.31.71:8080/scan
kubesec scan deployment.yaml
或者使用容器环境执行检查
docker run -i kubesec/kubesec scan /dev/stdin < deployment.yaml
kubesec内置一个HTTP服务器,可以直接启用,远程调用。
• 二进制
kubesec http 8080 &
• Docker容器
docker run -d -p 8080:8080 kubesec/kubesec http 8080
示例:
curl -sSX POST --data-binary @deployment.yaml http://192.168.31.71:8080/scan
准入控制器Admission Webhook
Admission Webhook:准入控制器Webhook是准入控制插件的一种,用于拦截所有向APISERVER发送的请求,并且可以修改请求或拒绝请求。
Admission webhook为开发者提供了非常灵活的插件模式,在kubernetes资源持久化之前,管理员通过程序可以对指定资源做校验、修改等操作。例如为资源自动打标签、pod设置默认SA,自动注入sidecar容器等。
Admission webhook为开发者提供了非常灵活的插件模式,在kubernetes资源持久化之前,管理员通过程序可以对指定资源做校验、修改等操作。例如为资源自动打标签、pod设置默认SA,自动注入sidecar容器等。
相关Webhook准入控制器:
• MutatingAdmissionWebhook:修改资源,理论上可以监听并修改任何经过ApiServer处理的请求
• ValidatingAdmissionWebhook:验证资源
• ImagePolicyWebhook:镜像策略,主要验证镜像字段是否满足条件
• MutatingAdmissionWebhook:修改资源,理论上可以监听并修改任何经过ApiServer处理的请求
• ValidatingAdmissionWebhook:验证资源
• ImagePolicyWebhook:镜像策略,主要验证镜像字段是否满足条件
准入控制器: ImagePolicyWebhook
工作流程
1、启用准入控制插件
--enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
--admission-control-config-file=/etc/kubernetes/image-policy/admission_configuration.yaml
并使用hostpath数据卷将宿主机/etc/kubernetes/image-policy目录挂载到容器中
--enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
--admission-control-config-file=/etc/kubernetes/image-policy/admission_configuration.yaml
并使用hostpath数据卷将宿主机/etc/kubernetes/image-policy目录挂载到容器中
2、准备配置文件
# /etc/kubernetes/image-policy/admission_configuration.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
configuration:
imagePolicy:
kubeConfigFile: /etc/kubernetes/image-policy/connect_webhook.yaml # 连接镜像策略服务器配置文件
allowTTL: 50 # 控制批准请求的缓存时间,单位秒
denyTTL: 50 # 控制批准请求的缓存时间,单位秒
retryBackoff: 500 # 控制重试间隔,单位毫秒
defaultAllow: true # 确定webhook后端失效时的行为
# /etc/kubernetes/image-policy/admission_configuration.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
configuration:
imagePolicy:
kubeConfigFile: /etc/kubernetes/image-policy/connect_webhook.yaml # 连接镜像策略服务器配置文件
allowTTL: 50 # 控制批准请求的缓存时间,单位秒
denyTTL: 50 # 控制批准请求的缓存时间,单位秒
retryBackoff: 500 # 控制重试间隔,单位毫秒
defaultAllow: true # 确定webhook后端失效时的行为
2、准备配置文件
# /etc/kubernetes/image-policy/kubeconfig.yaml
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /etc/kubernetes/image-policy/webhook.pem # 数字证书,用于验证远程服务
server: https://192.168.31.73:8080/image_policy # 镜像策略服务器地址,必须是https
name: webhook
contexts:
- context:
cluster: webhook
user: apiserver
name: webhook
current-context: webhook
preferences: {}
users:
- name: apiserver
user:
client-certificate: /etc/kubernetes/image-policy/apiserver-client.pem # webhook准入控制器使用的证书
client-key: /etc/kubernetes/image-policy/apiserver-client-key.pem # 对应私钥证书
# /etc/kubernetes/image-policy/kubeconfig.yaml
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /etc/kubernetes/image-policy/webhook.pem # 数字证书,用于验证远程服务
server: https://192.168.31.73:8080/image_policy # 镜像策略服务器地址,必须是https
name: webhook
contexts:
- context:
cluster: webhook
user: apiserver
name: webhook
current-context: webhook
preferences: {}
users:
- name: apiserver
user:
client-certificate: /etc/kubernetes/image-policy/apiserver-client.pem # webhook准入控制器使用的证书
client-key: /etc/kubernetes/image-policy/apiserver-client-key.pem # 对应私钥证书
3、部署镜像服务器
自己用python开发一个简单的webhook端点服务器,作用是拒绝部署的镜像乜有指定标签(即latest)。
3.1 自签HTTPS证书
3.2 Docker容器启动镜像策略服务
docker run -d -u root --name=image-policy-webhook \
-v $PWD/webhook.pem:/data/www/webhook.pem \
-v $PWD/webhook-key.pem:/data/www/webhook-key.pem \
-e PYTHONUNBUFFERED=1 -p 8080:8080 \
lizhenliang/image-policy-webhook
自己用python开发一个简单的webhook端点服务器,作用是拒绝部署的镜像乜有指定标签(即latest)。
3.1 自签HTTPS证书
3.2 Docker容器启动镜像策略服务
docker run -d -u root --name=image-policy-webhook \
-v $PWD/webhook.pem:/data/www/webhook.pem \
-v $PWD/webhook-key.pem:/data/www/webhook-key.pem \
-e PYTHONUNBUFFERED=1 -p 8080:8080 \
lizhenliang/image-policy-webhook
4、测试
kubectl create deployment web1 --image=nginx:1.16
kubectl create deployment web2 --image=nginx
kubectl create deployment web1 --image=nginx:1.16
kubectl create deployment web2 --image=nginx
0 条评论
下一页