Kubernetes中的Seccomp

举报
kaliarch 发表于 2022/08/19 22:45:14 2022/08/19
【摘要】 Kubernetes生态系统具有相当多的安全特性,以保持您的容器的安全和隔离。这里我将讨论安全计算模式(又名seccomp)特性,它侧重于限制容器能够执行的系统调用。为什么这很重要?嗯,容器实际上只是一个在给定机器内运行的进程。它与所有其他应用程序共享内核。如果所有容器都有能力进行任何系统调用,恶意程序很快就会绕过容器隔离并影响其他应用程序–窃听信息、更改系统级设置等。您的seccomp配置...

Kubernetes生态系统具有相当多的安全特性,以保持您的容器的安全和隔离。这里我将讨论安全计算模式(又名seccomp)特性,它侧重于限制容器能够执行的系统调用。
为什么这很重要?嗯,容器实际上只是一个在给定机器内运行的进程。它与所有其他应用程序共享内核。如果所有容器都有能力进行任何系统调用,恶意程序很快就会绕过容器隔离并影响其他应用程序–窃听信息、更改系统级设置等。
您的seccomp配置文件定义了应该允许或阻止哪些系统调用,容器运行时将在容器启动时应用,以便内核可以强制执行。一旦应用,您将有效地减少您的攻击面,并在您的容器内的任何东西(即您的依赖项,或它们的依赖项……)开始做一些他们不应该被允许的事情时限制损害。

基础信息

一个基本的seccomp有三个关键元素:defaultAction、体系结构(或archMap)和系统组件:


{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "arch_prctl",
                "sched_yield",
                "futex",
                "write",
                "mmap",
                "exit_group",
                "madvise",
                "rt_sigprocmask",
                "getpid",
                "gettid",
                "tgkill",
                "rt_sigaction",
                "read",
                "getpgrp"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

defaultAction定义默认情况下未在syscalls部分中列出的任何系统调用将发生什么。为了简单起见,让我们关注将使用的两个主要值:SCMP_ACT_ERRNO,它将阻止系统调用的执行;SCMP_ACT_ALLOW执行它在TIN中所说的内容。
元素体系结构定义了您的目标体系结构。这很重要,因为在内核级别应用的实际筛选器是基于系统调用ID,而不是您在配置文件中定义的名称。容器运行时将在应用它之前将其转换为IDs。这一点的重要性在于,系统调用可能有不同的ID,这取决于它们所运行的体系结构。例如,用于从套接字接收信息的系统callrecvfrom在x64系统中的id为45,而在x86中的id为517。下面是x86-x64的所有系统调用列表。
syscalls是列出所有系统调用以及与它们相关联的操作的地方。例如,可以通过将defaultAction设置为SCMP_ACT_ERRNO,并将syscalls节中的操作设置为scmp_act_allow来创建白名单。这样,您将列出所有枚举的调用,并阻止其他所有调用。对于黑名单方法,恢复defaultAction和Action的值。

现在让我们切换档位,通过可能不那么明显的部分。但是在此之前,请注意下面的建议假设您正在将业务线应用程序部署到Kubernetes,并且以最少的权限运行对您来说一定很重要。

1.AllowPrivilegeEscalation=false

在容器的SecurityContext中有一个名为AllowPrivilegeEscalation的设置。当此设置为false时,容器将运行no_new_priv位。这有效地完成了它在tin中所说的,它阻止容器以比它自己更多的权限跨越新进程。
当此设置为true(这是默认值)时,另一个副作用是容器运行时将在容器启动过程的早期应用您的seccomp配置文件。因此,运行时内部进程运行所需的所有系统组件,如设置容器用户/组ID和删除功能,都必须在您的配置文件中被白名单化。
因此,对于只执行echo hi的容器,您需要以下内容:

{
    "defaultAction": "SCMP_ACT_ERRNO",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "arch_prctl",
                "brk",
                "capget",
                "capset",
                "chdir",
                "close",
                "execve",
                "exit_group",
                "fstat",
                "fstatfs",
                "futex",
                "getdents64",
                "getppid",
                "lstat",
                "mprotect",
                "nanosleep",
                "newfstatat",
                "openat",
                "prctl",
                "read",
                "rt_sigaction",
                "statfs",
                "setgid",
                "setgroups",
                "setuid",
                "stat",
                "uname",
                "write"
            ],
            "action": "SCMP_ACT_ALLOW"
        }
    ]
}

但话说回来,为什么这是一个问题?如果我不使用这些系统代码:capset、set_tid_address、setgid、setgroups和setuid,我个人会避免将它们列入白名单。然而,真正的问题是,由于需要将您完全无法控制的进程列入白名单,因此将您的概要文件与容器运行时实现绑定在一起。这意味着,您(或者很可能是您的云提供商)更新了您的容器运行时,突然您的容器不能再启动了。
专业技巧1:使用AllowPrivilegeEscaltion=false运行容器。它将使您的seccomp配置文件更小,并且不太可能受到容器运行时更改的影响。

2.在容器级别设置seccomp配置文件

在设置seccomp配置文件时,您可以选择在pod级别设置它:

annotations:
  seccomp.security.alpha.kubernetes.io/pod: "localhost/profile.json"

或在容器级别:

annotations:
  container.security.alpha.kubernetes.io/<container-name>: "localhost/profile.json"

请注意,当Kubernetes seccomp变成GA时,上面的语法将会改变。
一件不为人所知的事情是,在历史上,Kubernetes总是有一个bug,它迫使seccomp配置文件应用于暂停容器。尽管运行时将它抽象出来,但您的吊舱确实有这个容器,因为它是用来设置吊舱基础结构的。
问题是,容器总是以AllowPrivilegeEscalation=true执行,这导致了我们在第1点上讨论的相同问题,您没有办法改变这一点。
通过在容器级别设置seccomp,可以避免这个陷阱,并且能够创建一个主要关注容器的配置文件。直到bug被修复和一个新版本(可能是1.18?)用户可以广泛使用。
专业技巧2:在容器级别设置seccomp配置文件。
作为经验法则,这一点通常是对以下问题的一个非常好的回答:“为什么我的seccomp配置文件在docker运行时工作,但在部署到Kubernetes集群时却不工作?”

3.使用运行时/默认作为最后手段

Kubernetes目前有两个内置配置文件选项:运行时/default和docker/default。两者都是由容器运行时实现的,而不是由Kubernetes实现的。因此,它们可能会根据您使用的运行时/版本而有所不同。
因此,通过简单地更改运行库,您的容器可能会有一组不同的系统调用,它可能会使用,也可能不会使用。docker实现是大多数运行时使用的,如果您想使用这个配置文件,请确保您对它所包含的内容感到满意。
Docker/Default配置文件自Kubernetes1.11以来就不推荐使用,所以请避免使用它。
在我看来,runtime/default配置文件非常适合创建它的目的:保护用户在他们自己的机器上运行docker run命令,并可能使他们的机器受到损害。然而,当谈到运行在Kubernetes集群中的业务应用程序时,我认为这样的概要文件太开放了,开发人员应该专注于创建特定于应用程序(或应用程序类型)的概要文件。

专业技巧3:创建特定于应用程序的seccomp配置文件。如果做不到这一点,可以选择应用程序类型seccomp配置文件,例如创建一个包含所有golang web api应用程序的超集配置文件。作为最后手段,使用runtime/default。
在以后的文章中,我将介绍如何以SecDevOpsy的方式创建seccomp配置文件,并通过您的管道对它们进行自动化和测试。这样,你就不会有借口不去应用程序特定的配置文件。

4.无拘束不应成为一种选择

Kubernetes的第一次安全审计发现的一件事是seccomp在默认情况下是禁用的。这意味着,除非您在集群中创建一个启用它的PodSecurityPolicy,否则所有未指定seccomp配置文件的pods都将自动以seccomp=unconcined运行。
在此模式下运行意味着少了一个隔离层来保护您的群集,安全社区建议不要这样做。
专业提示#4:集群中的任何容器都不应该以seccomp=unconfined的形式运行,特别是在生产环境中。

5. “Audit mode”

这一点并不是Kubernetes独有的,但它属于“你甚至在开始之前就应该知道的事情”。:)
从历史上看,创建seccomp配置文件是一个痛苦的过程,而且主要是基于反复试验和错误。这并不是说它发生了很大的变化,而是您没有办法在生产环境中测试它而不冒破坏应用程序的风险。
自从linux Kernel4.14以来,现在可以定义配置文件的某些部分以审计模式运行,登录到syslog中所需的所有系统调用而不阻塞它们。为此,可以使用操作SCMT_ACT_LOG:
scmp_act_log:seccomp筛选器对线程调用没有影响
如果系统调用与任何已配置的seccomp不匹配,则为
筛选规则,但系统调用将被记录。
使用这种方法的一个好策略是:

  • 允许您知道需要的系统调用。
  • 阻止您知道不需要的系统调用。
  • 把其他的都记录下来。

一个简单的例子如下所示:

{
    "defaultAction": "SCMP_ACT_LOG",
    "architectures": [
        "SCMP_ARCH_X86_64",
        "SCMP_ARCH_X86",
        "SCMP_ARCH_X32"
    ],
    "syscalls": [
        {
            "names": [
                "arch_prctl",
                "sched_yield",
                "futex",
                "write",
                "mmap",
                "exit_group",
                "madvise",
                "rt_sigprocmask",
                "getpid",
                "gettid",
                "tgkill",
                "rt_sigaction",
                "read",
                "getpgrp"
            ],
            "action": "SCMP_ACT_ALLOW"
        },
        {
            "names": [
                "add_key",
                "keyctl",
                "ptrace"
            ],
            "action": "SCMP_ACT_ERRNO"
        }
    ]
}

但是请记住,您必须阻止您知道的所有调用,因为您不会使用某个事实,并且可能会对您的集群造成潜在的损害。获得列表的一个很好的来源是官方的docker文档,其中他们解释了在默认配置文件中阻止了哪些系统调用以及原因。
但有个陷阱!尽管SCMT_ACT_LOG自2017年的ebd以来就得到了内核的支持,但它只是最近才进入Kubernetes生态系统。因此,要使用它,您需要至少运行在Linux内核4.14和runC版本V1.0.0-RC9上。
专业技巧#5:通过混合黑名单和白名单并记录所有异常,创建审计模式配置文件以在生产中进行测试。

6.白名单

白名单在这个过程中增加了额外的工作,因为您需要识别应用程序可能进行的每个系统调用,以便登陆您的配置文件,但它确实增加了额外的安全层:
强烈建议尽可能使用白名单方法,因为这样的方法更加健壮和简单。每当添加潜在危险的系统调用时,就必须更新黑名单(如果这些调用被列入黑名单,则更新一个危险标志或选项),而且通常可以在不改变其含义的情况下更改值的表示形式,从而绕过黑名单。
对于go应用程序,我开发了一个工具,遍历执行路径并提取所有系统调用。以下申请:

package main

import "fmt"

func main() {
	fmt.Println("test")
}

通过对其运行gosystract:

go install https://github.com/pjbgf/gosystract
gosystract --template='{{- range . }}{{printf "\"%s\",\n" .Name}}{{- end}}' application-path

以下是您得到的:

"sched_yield",
"futex",
"write",
"mmap",
"exit_group",
"madvise",
"rt_sigprocmask",
"getpid",
"gettid",
"tgkill",
"rt_sigaction",
"read",
"getpgrp",
"arch_prctl",

稍后我会更多地介绍工具,这只是一个开始。
专业技巧#6:允许你知道你需要的系统调用,阻止其他一切。

7.正确掌握基本知识或面对意想不到的行为

无论您在seccomp配置文件中定义什么,内核都将强制执行。即使那不是你想要的。例如,如果阻止对exit或exit_group等调用的访问,容器可能无法退出,而简单的“echo hi”就可能无限期地将容器困在退出循环中。导致集群的CPU使用率高:

在这种情况下,strace可以派上用场,并向您展示可能出现的问题:

确保您的配置文件很好地反映了应用程序执行路径中的所有系统调用。
专业提示#7:要全面,确保所有的基本系统调用都被白名单化了。
这是关于以SecDevOpsy方式在Kubernetes中实现seccomp。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。