基于开源OPA实现Kubernetes资源安全合规基线检查运维进阶

近年来越来越多的企业选择使用容器用于软件构建和发布,随着DockerSwarm的逐渐没落,毫无疑问Kubernetes已经成为事实上的容器编排工具标准。

目前使用Kubernetes已经不是什么大的难题,基本上几个命令几个yaml文件就能轻易地使应用运行在Kubernetes平台上。

比如:

而解决如上问题,最有效的方法就是设置准入门槛,不符合规范的请求直接拒绝,而不是先污染后治理。

Kubernetes一开始设计就支持通过准入控制器(AdmissionControllers)方案设置准入门槛,官方也提供了许多现成的准入控制器供用户选择使用。

但是不同的企业可能安全准则、应用最佳实践标准并不一样,现有的准入控制器可能无法完全满足企业的所有需求,重新自研开发控制器则涉及开发成本高,并且后期也不易于维护扩展。

因此,我认为最有效的办法是能通过统一标准的规则引擎实现合规基线检测和修正,针对不同的规范要求,只需要编写对应规则动态注入,无需大量开发控制器插件,无需重启Kubernetes平台。

本文接下来将介绍基于开源OPA实现Kubernetes资源合规基线检查方案。

虽然本文主要介绍如何使用OPA执行Kubernetes资源合规基线检查,但其原理离不开Kubernetes的准入控制,因此本文首先简单介绍下Kubernetes自带的准入控制机制。

众所周知,我们每次请求KubernetesAPI时都会经过APIServer的三大核心关口,层层把关:

kubeapi_pilelines

同理,Kubernetes可以配置多个准入控制器,只有所有准入控制器都判定通过时,请求方可放行。

另外准入控制器还支持对请求的资源进行修改,比如AlwaysPullImages准入控制器会强制修改Pod的imagePullPolicy为Always。

更多的准入控制器可以参考官方文档UsingAdmissionControllers[1]。当然用户也可以根据自己的需求实现自定义准入控制器。

opa-service

比如最典型的基于角色的权限控制(RBAC)就可以抽象成策略模型:定义各个角色具有哪些权限,通过规则判断用户隶属于哪个角色,从而判定该用户具备哪些权限,比如:

还有一个典型的场景便是安全合规性检测,比如我们定义规则Kubernetes的所有Pod不允许使用latest标签镜像,通过这个规则可以查找出所有包含latest标签的违规Pod。我们还可以把这个规则设置为准入门槛,每次创建Pod前,都会通过我们的规则去做评审(Review),如果包含latest镜像则直接拒绝服务。

而OPA的设计理念则是软件服务本体与策略、策略与决策完全松耦合,换句话说,通过标准统一的策略决策,与检测体完全独立,只要检测对象能够转化为标准输入数据源即可。

官方提供了几个典型的应用场景:

使用OPA,最核心的工作便是编写规则,而OPA使用的是Rego语言进行规则定义,因此不妨先简单介绍下Rego。

关于Rego的详细语法可以参考官方文档policy-language[13],这里只简单介绍一些最简单的语法。

Rego最基本的规则rule模板格式如下:

x=y{expr_1expr_2...}如上所有表达式expr均为AND关系,即当且仅当所有的expr判定结果为true时,x会被置为y。

比如下面这条Rule,当x>y且x>z时,x_is_greatest为true:

x_is_greatest=true{x>yx>z}后面变量赋值=true为默认值,可以省略,因此如下写法的效果是完全一样的:

x_is_greatest{x>yx>z}这有点类似Python的语法:

defaultallow=falseallow{input.user=="admin"}allow{input.user=="Jim"input.method=="GET"}如上定义了两个相同的Ruleallow,当满足任意两个条件之一时,allow为true:

因此有如下判定结果:

allowwithinputas{"user":"admin","method":"POST"}#trueallowwithinputas{"user":"Jim","method":"GET"}#trueallowwithinputas{"user":"Jim","method":"POST"}#false除了OR,Rego也不像大多数编程语言一样具有丰富的分支选择语法(if-else),但是可以通过相同的方式实现if-else逻辑,比如实现f(x)函数:

f(x)="A"{x>=90}f(x)="B"{x>=80;x<90}f(x)="C"{x>=70;x<80}f(x)="D"{x<70}前面介绍的语法还是比较容易理解的,Rego与大多数编程语言在使用方面最大的差别在于集合的操作以及迭代遍历,初次使用会感觉特别奇怪。

比如arr[i],如果i为已定义的变量,则与大多数编程语言语义相同,都是取数组对应索引的值。

但是当i未定义时,则表示遍历数组的索引和值,类似Python的enumerate()函数,在后面的迭代过程中可以引用索引值i以及对应数组的值。

>arr:=["a","b","a","c"]>arr[i]+---+--------+|i|arr[i]|+---+--------+|0|"a"||1|"b"||2|"a"||3|"c"|+---+--------+>i:=2>arr[i]"a"特别地,当变量名为_时,则表示忽略索引值:

>arr:=["a","b","a","c"]>arr[_]+--------+|arr[_]|+--------+|"a"||"b"||"a"||"c"|+--------+因此在Rego中遍历数组的方法如下:

containers:=["int32bit.com/nginx:v1.7","dockerhub.com/mysql:5.7"]container:=containers[_]notstartswith(container,"int32bit.com/")...注:如上空格缩进仅为方便理解,实际编写时不需要缩进。

语义类似Python的如下写法:

containers=["int32bit.com/nginx:v1.7","dockerhub.com/mysql:5.7"]forcontainerincontainers:ifnotcontainer.startswith("int32bit.com/"):...当然新版本的Rego也支持了in语法,如上代码可等效为:

根据这种语法进一步引申,判断元素是否包含在列表中,看起来也会比较奇怪:

>arr:=["a","b","c","b"]>arr[i]=="c"#如果i变量未定义,则会遍历数组,返回所有值为c的索引列表+---+|i|+---+|2|+---+>i:=1>arr[i]=="c"#i已定义为数字1,则判断指定索引1中的值是否为cfalse引入了集合后,派生出一种新的规则rule语法,样例如下:

containers:=["int32bit.com/nginx:v1.7","dockerhub.com/mysql:5.7"]deny_images[c]{container:=containers[_]notstartswith(container,"int32bit.com/")c:=container.image}如上的deny_images是一个列表,语义为遍历containers列表,找出不是以int32bit.com/为前缀的元素,加到deny_images中,等效Python语法:

containers=["int32bit.com/nginx:v1.7","dockerhub.com/mysql:5.7"]deny_images=[]forcontainerincontainers:ifnotcontainer.startswith("int32bit.com/"):c=containerdeny_images.append(c)为什么会有如上奇怪的语法?个人感觉Rego的语法虽然理解起来可能比较绕,但是针对集合操作与查询写起来会比Python更简练些。

与Python一样,Rego也支持集合推导(Comprehension),比如:

arr:=["a","b","c","a","c","d"]s:={i|i=arr[_]}等价于Python:

arr=["a","b","c","a","c","d"]s={iforiinarr}除了以上的语法,Rego集成了非常丰富的内置函数,可以参考Regobuilt-in-functions[14],关于Rego的更多详细语法可以参考官方文档policy-language[15]。

前面介绍了Kubernetes的准入控制以及通用策略规则引擎工具OPA。显然,OPA是非常适合用于实现Kubernetes资源的安全合规基线配置检查的。

针对这个场景,OPA官方已提供了现成的集成方案OPAGatekeeper[16],该方案通过Webhook准入控制器方式集成。

可以认为OPAGatekeeper是一个代理Web服务,KubernetesAPIServer通过Webhook准入控制器将请求体转发给OPAGatekeeper评审,请求Body样例如下:

{"kind":"AdmissionReview","request":{"kind":{"kind":"Pod","version":"v1"},"object":{"metadata":{"name":"myapp"},"spec":{"containers":[{"image":"nginx","name":"nginx-frontend"},{"image":"mysql","name":"mysql-backend"}]}}}}OPAGatekeeper基于Repo规则对请求的Body进行判定,并返回给APIServer。

kubernetesadmissionflow

关于OPAGatekeeper的安装配置可以参考官方文档OPAgatekeeperinstall[17],如果只需要默认配置,简单只需要一条apply命令即可:

以一个简单的场景为例,假设我们要求所有的资源必须包含指定的labels,则可定义模板如下:

kind:ConstraintTemplatemetadata:name:k8srequiredlabelsspec:crd:spec:names:kind:K8sRequiredLabelsvalidation:openAPIV3Schema:properties:labels:type:arrayitems:stringtargets:-target:admission.k8s.gatekeeper.shrego:|packagek8srequiredlabelsviolation[{"msg":msg,"details":{"missing_labels":missing}}]{provided:={label|input.review.object.metadata.labels[label]}required:={label|label:=input.parameters.labels[_]}missing:=required-providedcount(missing)>0msg:=sprintf("youmustprovidelabels:%v",[missing])}constrainttemplates模板定义包含两部分:

前面的Rego例子不难理解,首先取出资源Object的labels列表,然后取出要求必须配置的labels列表,最后二者取差集就是缺失的labels列表。

配置了模板后,需要实例化constraint,引用前面的constrainttemplate并配置输入参数:

apiVersion:constraints.gatekeeper.sh/v1beta1kind:K8sRequiredLabelsmetadata:name:namespace-labels-checkspec:match:kinds:-apiGroups:[""]kinds:["Namespace"]parameters:labels:["app_name","org_name"]constraint定义包含两个主要部分:

创建如上资源后,我们尝试创建没有任何label的namespace:

#kubectlcreatensaaaErrorfromserver:youmustprovidelabels:{"app_name","org_name"}admissionwebhook"validation.gatekeeper.sh"deniedtherequest符合预期创建namespace失败,错误中提示没有配置app_name以及org_name标签。

前面我们设置了K8sRequiredLabelsconstraint,当我们创建没有打app_name以及org_name标签的不合规的namespace时会直接拒绝请求。

但是往往在创建K8sRequiredLabelsconstraint之前已经有很多存量的namespaces了,如何查看哪些存量namespaces不合规呢?

OPAGatekeeper提供了如下三种方式:

如下,我们可以查看所有不满足namespace-labels-check的违规namespaces列表:

当我们的规则限制作用于Pod时,则无法直接拒绝创建违规的Deployments或者DaemonSets,但创建的Deployment会创建Pod失败。

比如我们创建如下的constraint实例,要求所有的Pod必须包含app_name以及org_name标签。

apiVersion:constraints.gatekeeper.sh/v1beta1kind:K8sRequiredLabelsmetadata:name:pod-labels-checkspec:match:kinds:-apiGroups:[""]kinds:["Pod"]parameters:labels:["app_name","org_name"]我们创建nginx-appDeployment:

apiVersion:apps/v1kind:Deploymentmetadata:labels:app:nginx-appname:nginx-appspec:replicas:3selector:matchLabels:app:nginx-apptemplate:metadata:labels:app:nginx-appspec:containers:-image:nginx:latestname:nginx此时Deployment创建成功,但是Pod不会被创建出来:

#kubectlgetdeployments.appsNAMEREADYUP-TO-DATEAVAILABLEAGEnginx-app0/3003m49s我们可以查看最近的Event:

#kubectlgeteventsLASTSEENTYPEREASONOBJECTMESSAGE2m31sWarningFailedCreatereplicaset/nginx-app-78b5f9b5fdErrorcreating:admission"pod-label-check"deniedtherequest:youmustprovidelabels:{"app_name","org_name"}可见是由于pod-label-check阻断了Pod资源创建。当然我们也可以通过status获取阻断原因:

status:conditions:-lastTransitionTime:"2021-12-30T10:15:32Z"lastUpdateTime:"2021-12-30T10:15:32Z"message:'admissionwebhook"validation.gatekeeper.sh"deniedtherequest:[pod-labels-check]youmustprovidelabels:{"app_name","org_name"}'reason:FailedCreatestatus:"True"type:ReplicaFailure5.5违规处理前面当我们创建不合规的资源时会直接拒绝请求,这是因为OPA的默认违规处理方式是deny,即拒绝请求。

除了deny阻断模式,OPAGatekeeper还支持如下两种违规处理策略:

以warn为例,我们创建一个违规Pod:

#kubectlapply-fpod.yamlWarning:[prod-repo-is-openpolicyagent]containerhasaninvalidimagerepo"test.com",allowedreposare["int32ibt.com"]pod/pausecreated我们发现Pod创建成功,但是kubectl返回了Warning信息,提示Pod使用了违规的镜像仓库。

Gatekeeper除了支持基于规则执行违规资源检测,还支持对违规资源进行自动修复。不过目前该功能还不是很完善,只支持静态赋值,不支持动态配置。

比如我们要求除kube-systemnamespace以外的所有Pod的imagePullPolicy必须为Always,则可以创建如下Assign实例:

apiVersion:mutations.gatekeeper.sh/v1beta1kind:Assignmetadata:name:set-image-pull-policy-to-alwaysspec:applyTo:-groups:[""]kinds:["Pod"]versions:["v1"]match:scope:Namespacedkinds:-apiGroups:["*"]kinds:["Pod"]excludedNamespaces:["kube-system"]location:"spec.containers[name:*].imagePullPolicy"parameters:assign:value:Always另外一个场景是不允许用户设置Pod的securityContext为privileged,则可以通过自动设置securityContext.privileged为false实现。

apiVersion:mutations.gatekeeper.sh/v1beta1kind:Assignmetadata:name:demo-privilegedspec:applyTo:-groups:[""]kinds:["Pod"]versions:["v1"]match:scope:Namespacedkinds:-apiGroups:["*"]kinds:["Pod"]namespaces:["bar"]location:"spec.containers[name:foo].securityContext.privileged"parameters:assign:value:false5.7开源的模板仓库在Github中官方维护了一些很有用的现成模板gatekeeper-library[18],内容还是很全的,涵盖安全、配置基线等最佳实践规则。

本文首先简单介绍了Kubernetes的资源治理问题以及准入控制器,然后介绍了OPA以及Rego,最后介绍如何将OPA集成到Kubernetes的准入控制器中实现Kubernetes资源的安全合规自动检测。

THE END
1.开源阅读软件10000个书源分享开源阅读是一款支持自定义导入书源的平台,在这款app中用户可以导入各种自己喜欢的书源来看到各个平台上面的作品,今天小编就为大家带来开源阅读最新软件10000个可用书源分享,各位可以自由选择导入,绝对让你看到爽,有需要的朋友们快来看一看吧! 使用说明 提示导入失败,原因就是网络的问题,大家可以切换无线网络或者是挂T子http://www.kaiweituopan.com/kaiwei03/4256dd58b.html
2.开源阅读app手机版下载开源阅读app移动版下载v3.25开源阅读App是一款专为阅读爱好者打造的、高度可定制化的电子书阅读器,它支持多种电子书格式,提供丰富的阅读设置选项,以及强大的书源管理功能,让用户能够轻松访问并阅读海量的在线书籍资源。 【开源阅读app简介】 开源阅读App以其开源特性闻名,这意味着用户可以自由查看、修改和分享其源代码。这不仅促进了软件的不断https://m.xueba5.com/soft/294949.html
3.开源资源阅读电脑版开源资源阅读电脑版官方下载[含模拟器]开源资源阅读电脑版是一款非常不错的编程学习平台,里面汇聚着大量的编程爱好者,在这里你可以查看自己喜欢的编程项目,也可以收藏别人的项目,可以在一起相互交流先进经验,共同进步。华军软件园提供开源资源阅读电脑版的官方下载地址,有需要的用户赶快下载吧! 开源资源阅读电脑版介绍: 这是一个用来查看GitHub最受欢迎与https://m.onlinedown.net/soft/10103993.htm
4.开源阅读app下载安装开源阅读软件最新版下载开源阅读app是一款免费的小说阅读神器,用户们能够在这款APP上获得各种不同小说的免费资源,且在阅读的过程中用户不会受到各种乱七八糟的广告干扰,同时,该软件中的小说资源十分丰富,有当下热门的连载类小说,也有已经完结的爆款小说,此外,软件还支持用户自由的更改阅读的模式或者是背景等,会给用户们带来较为舒适且便利http://www.downcc.com/k/kyydapp/
5.开源阅读app书源源码开源阅读App书源源码是指该阅读应用程序中用于提供电子书资源的源代码。开源意味着源代码是公开的,并且可以由任何人自由使用、修改和分发。 开源阅读App的书源源码具有以下几个重要的优势: 自由定制:使用开源书源源码,开发人员可以根据自己的需求自由定制和修改书源。无论是增加、删除、更改书源,还是对获取和解析电子书的https://wenku.csdn.net/answer/565d3mrs36
6.开源阅读怎么添加书源阅读书源最新免费资源2023开源阅读怎么添加书源 阅读书源最新免费资源2023 阅读3.0版本,无广告界面干净,自定义程度巨巨巨巨高,但它强大之处不在于软件,而在于是否有优秀的书源。下载后是一片空白,需要自己导入书源才能开始使用,以下都是可以网络导入的书源合集链接,使用方法很简单,选择复制文章内的地址,进入开源阅读app点击我的—书源管理—右上http://www.paihb.com/news/2882.html
7.阅读3.x开源阅读软件+书源10000个+阅读书源分享阅读是一款使用Kotlin全新开发的开源的阅读软件,如今的小说阅读软件总是在不断的添加广告,作为一个小说爱好者真是受不了,而使用开源的阅读软件就不用再担心这些问题,阅读3.0可以自定义来源阅读网络的内容,为广大网络文学爱好者提供一种方便、快捷舒适的阅读体验,并且更符合使用习惯。好人卡资源网 http://www.zzfmdn.com/article/866744
8.Legado开源阅读阅读3.0, Legado 开源阅读是一款可以自定义来源阅读网络内容的工具,为广大网络文学爱好者提供一种方便、快捷舒适的试读体验。「Legado Read 3.0, Legado is a tool that allows you to read web content from customized sources, providing a convenient, fast and comfortable trial reading experience for the majorihttps://www.worldlink.com.cn/osdir/legado.html
9.免费PC端小说阅读器ReadCat:开源简洁纯净无广告免费PC端小说阅读器ReadCat:开源、简洁、纯净、无广告 此内容为免费资源,请登录后查看 ¥0 登录查看 推荐使用支付宝支付,若微信支付后未成功获取资源,请立即站内私信或QQ联系管理员处理 免费资源已售18使用方法 ReadCat不携带插件,若您首次使用ReadCat且未导入书源插件,将无法获取任何在线书本内容 插件导入方法 功能https://www.zuitx.com/2600.html
10.diffsofancy:使差异易于阅读,而不是机器可读Th**er 上传45KB 文件格式 gz 开源软件 diff-so-fancy努力使您的diff易于阅读,而不是机器可读。 这有助于提高代码质量,并帮助您更快地发现缺陷。 diff-so-fancy也可以从NPM,Nix,brew以及Arch和Debian Linux上的软件包中获得。 Windows用户可能需要安装MinGW或Linux的Windows子系统。 默认情况下,文件头的分隔符https://www.coder100.com/index/index/content/id/3631601
11.汉斯期刊开放获取汉斯出版社 聚焦于国际开源学术期刊,电子期刊,期刊投稿,中文期刊,开源期刊的出版发行, 覆盖数学物理、生命科学、化学材料、地球环境、医药卫生、工程技术等领域。https://www.hanspub.org/
12.开源视频资源播放器ZY Player是国人开源的一款跨平台视频资源播放器。基于VUE编写,遵守MIT开源协议。 功能特色: 全平台支持. Windows, Mac, Linux 支持IPTV, 卫视直播 视频源支持自定义, 支持导入, 导出 支持海报模式和列表模式浏览资源 播放历史, 自动跳转历史进度 收藏夹支持导入,导出, 支持同步追剧 http://osp.io/archives/6829
13.证券开源阅读书源合集最新版社会新闻11月29日,为什么不建议买太便宜的洗衣液,免费股票在线咨询,领取50000迷你币兑换码,草莓视频官网招生电话,梦幻西游开疆拓土安卓版,李玉你混蛋啊哈,药效h1v2司墨,造梦西游三还童丹怎么获得,ysl口红水蜜桃色号2425,高中数学1v1补课班txt,啦啦啦资源在线观看5,成色好的y31s是国产吗。 11月29日,养老院http://m.5iecity.net.cn/know/windows/20241129/202802.shtml
14.书栈网·BookStack书栈网,IT程序员互联网开源编程书籍阅读分享,囊括小程序、前端、后端、移动端、云计算、大数据、区块链、机器学习、人工智能和面试笔试等相关书籍,助你【码】力十足!https://www.bookstack.cn/