大咖直播-鸿蒙原生开发与智能提效实战!>>> 了解详情
写点什么

Rego 不好用?用 Pipy 实现 OPA

作者: Addo Zhang

  • 2021-07-15
  • 本文字数:5099 字

    阅读完需:约 17 分钟

Rego 不好用?用 Pipy 实现 OPA

还不知道 Pipy 是什么的同学可以先看下简介:


Pipy 是一个轻量级、高性能、高稳定、可编程的网络代理。Pipy 核心框架使用 C++ 开发,网络 IO 采用 ASIO 库。 Pipy 的可执行文件仅有 5M 左右,运行期的内存占用 10M 左右,因此 Pipy 非常适合做 Sidecar proxy。


Pipy 内置了自研的 pjs 作为脚本扩展,使得 Pipy 可以用 JS 脚本根据特定需求快速定制逻辑与功能。

Pipy 采用了模块化、链式的处理架构,用顺序执行的模块来对网络数据块进行处理。这种简单的架构使得 Pipy 底层简单可靠,同时具备了动态编排流量的能力,兼顾了简单和灵活。通过使用 REUSE_PORT 的机制(主流 Linux 和 BSD 版本都支持该功能),Pipy 可以以多进程模式运行,使得 Pipy 不仅适用于 Sidecar 模式,也适用于大规模的流量处理场景。 在实践中,Pipy 独立部署的时候用作“软负载”,可以在低延迟的情况下,实现媲美硬件的负载均衡吞吐能力,同时具有灵活的扩展性。


在玩过几次 Pipy 并探究其工作原理后,又有了更多的想法。


初探可编程网关 Pipy可编程网关 Pipy 第二弹:编程实现 Metrics 及源码解读可编程网关 Pipy 第三弹:事件模型设计


在使用 OPA 的时候,一直觉得 Rego 不是那么顺手,使用 pipy js 来写规则的想法油然而生。今天就一起试试这个思路。果然,不试不知道,一试发现太多的惊喜~Pipy 不止于“代理”,更有很多可以适用的场景:


•极小的单一可执行文件(single binary)使得 pipy 可能是最好的 “云原生 sidecar”

•sidecar 不仅仅是代理,还可以做控制器,做运算单元

•proxy 的串路结构适合各种管控类的操作,比如访问控制

•Pipy js 的扩展能力和快速编程能力,很适合做 “规则引擎”,或者用最近流行的说法 “云原生的规则引擎”。对比 OPA 我认为它完全够格做一个 “羽量级规则执行引擎”。


现在我更倾向于定义 pipy 是一个 “云原生的流量编程框架”,代理只是其底层的核心能力,叠加了 pipy js 以后,上层可以做的事情很多,“流量滋养万物”。


在 使用 Open Policy Agent 实现可信镜像仓库检查 之后,就在想 Pipy 是否一样可以做到,将内核替换成 Pipy + 规则。所以今天大部分内容和上面这篇是相似的。


来,一起看看这个“不务正业”的 Pipy 如何实现 Kubernetes 的准入控制器 来做镜像的检查。


环境


继续使用 minikube


minikube start
复制代码


创建部署 Pipy 的命名空间


kubectl create namespace pipykubens pipykubectl label ns pipy pipy/webhook=ignore #后面解释
复制代码


规则


在 OPA 中,通过 kube-mgmt 容器监控 configmap 的改动,将 Policy 推送到同 pod 的 opa 容器中。


对于 Pipy 为了渐变,直接使用挂载的方式将保存了规则的 configmap 挂载到 Pipy 的容器中。


实际的使用中,Pipy 支持轮训的方式检查控制平面中规则的变更,并实时加载;也可以实现与 OPA 的 kube-mgmt 同样的逻辑。


实现了上一讲功能的 pipy 规则如下:


cat > pipy-rule.js <<EOFpipy({  _repoPrefix: '192.168.64.1', //192.168.64.1:5000 是笔者本地容器运行的一个私有仓库。  _tagSuffix: ':latest',}).listen(6443, {  tls: {    cert: os.readFile('/certs/tls.crt').toString(),    key: os.readFile('/certs/tls.key').toString(),  },})  .decodeHttpRequest()  .replaceMessage(    msg => (        ((req, result, invalids, reason) => (            req = JSON.decode(msg.body),            invalids = req.request.object.spec.containers.find(container => (              (!container.image.startsWith(_repoPrefix) ? (                reason = `${container.image} repo not start with ${_repoPrefix}`,                console.log(reason),                true              ) : (false))              ||              (container.image.endsWith(_tagSuffix) ? (                reason = `${container.image} tag end with ${_tagSuffix}`,                console.log(reason),                true              ) : (false)            ))),            invalids != undefined ? (              result = {                "apiVersion": "admission.k8s.io/v1beta1",                "kind": "AdmissionReview",                "response": {                    "allowed": false,                    "uid": req.request.uid,                    "status": {                        "reason": reason,                    },                },              }            ) : (              result = {                "apiVersion": "admission.k8s.io/v1beta1",                "kind": "AdmissionReview",                "response": {                    "allowed": true,                    "uid": req.request.uid                },              }            ),            console.log(JSON.encode(result)),            new Message({              'status' : 200,              'headers': {                'Content-Type': 'application/json'              }              }, JSON.encode(result))        ))()    )  )  .encodeHttpResponse()  EOF
复制代码


将规则保存在 configmap 中:


kubectl create configmap pipy-rule --from-file=pipy-rule.js
复制代码


在 Kubernetes 上部署 Pipy


Kubernetes 与准入控制器(Admission Controller[3])的通信需要使用 TLS。配置 TLS,使用 openssl 创建证书颁发机构(certificate authority CA)和 OPA 的证书/秘钥对。


genrsa -out ca.key 2048req -x509 -new -nodes -key ca.key -days 100000 -out ca.crt -subj "/CN=admission_ca"
复制代码


为 OPA 创建 TLS 秘钥和证书:


cat >server.conf <<EOF[req]req_extensions = v3_reqdistinguished_name = req_distinguished_nameprompt = no[req_distinguished_name]CN = pipy.pipy.svc[ v3_req ]basicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuth, serverAuthsubjectAltName = @alt_names[alt_names]DNS.1 = pipy.pipy.svcEOF
复制代码


注意 CN 和 alt_names 必须与后面创建 Pipy service 的匹配。


openssl genrsa -out server.key 2048openssl req -new -key server.key -out server.csr -config server.confopenssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 100000 -extensions v3_req -extfile server.conf
复制代码


为 OPA 创建保存 TLS 凭证的 Secret:


kubectl create secret tls pipy-server --cert=server.crt --key=server.key
复制代码


将 Pipy 部署为准入控制器(admission controller)。为了方便调试,我们使用启动 Pipy 的时候打开了控制台。


kind: ServiceapiVersion: v1metadata:  name: pipy  namespace: pipyspec:  selector:    app: pipy  ports:  - name: https    protocol: TCP    port: 443    targetPort: 6443  - name: gui # 方便调试    protocol: TCP    port: 6060    targetPort: 6060  - name: http    protocol: TCP    port: 6080    targetPort: 6080---apiVersion: apps/v1kind: Deploymentmetadata:  labels:    app: pipy  namespace: pipy  name: pipyspec:  replicas: 1  selector:    matchLabels:      app: pipy  template:    metadata:      labels:        app: pipy      name: pipy    spec:      containers:         - name: pipy          image: pipy:latest          imagePullPolicy: IfNotPresent          args:            - "pipy"            - "/opt/data/pipy-rule.js"            - "--gui-port=6060" # 方便调试            # - "--log-level=debug"          ports:          - name: gui            containerPort: 6060            protocol: TCP          - name: http            containerPort: 6080            protocol: TCP            - name: https            containerPort: 6443            protocol: TCP          volumeMounts:            - readOnly: true              mountPath: /certs              name: pipy-server            - readOnly: false              mountPath: /opt/data              name: pipy-rule      volumes:        - name: pipy-server          secret:            secretName: pipy-server        - name: pipy-rule          configMap:            name: pipy-rule
复制代码


暴露控制台的访问:

kubectl expose deploy pipy --name pipy-node --type NodePortkubectl get svc pipy-portminikube service --url pipy-node -n pipy# 找到控制台端口
复制代码


接下来,生成将用于将 Pipy 注册为准入控制器的 manifest。


cat > webhook-configuration.yaml <<EOFkind: ValidatingWebhookConfigurationapiVersion: admissionregistration.k8s.io/v1beta1metadata:  name: pipy-validating-webhookwebhooks:  - name: validating-webhook.pipy.flomesh-io.cn    namespaceSelector:      matchExpressions:      - key: pipy/webhook        operator: NotIn        values:        - ignore    rules:      - operations: ["CREATE", "UPDATE"]        apiGroups: ["*"]        apiVersions: ["*"]        resources: ["pods"]    clientConfig:      caBundle: $(cat ca.crt | base64 | tr -d '\n')      service:        namespace: pipy        name: pipyEOF
复制代码


生成的配置文件包含 CA 证书的 base64 编码,以便可以在 Kubernetes API 服务器和 OPA 之间建立 TLS 连接。


kubectl apply -f webhook-configuration.yaml
复制代码


测试


pod-bad-repo.yaml:


apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-server  namespace: defaultspec:  containers:  - image: nginx:1.21.1    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
复制代码


kubectl apply -f pod-bad-repo.yamlError from server (nginx:1.21.1 repo not start with 192.168.64.1): error when creating "pod-bad-repo.yaml": admission webhook "validating-webhook.pipy.flomesh-io.cn" denied the request: nginx:1.21.1 repo not start with 192.168.64.1
复制代码


pod-bad-tag.yaml:


apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-server  namespace: defaultspec:  containers:  - image: 192.168.64.1:5000/nginx:latest    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
复制代码


kubectl apply -f pod-bad-tag.yamlError from server (192.168.64.1:5000/nginx:latest tag end with :latest): error when creating "pod-bad-tag.yaml": admission webhook "validating-webhook.pipy.flomesh-io.cn" denied the request: 192.168.64.1:5000/nginx:latest tag end with :latest
复制代码


pod-ok.yaml


apiVersion: v1kind: Podmetadata:  creationTimestamp: null  labels:    run: web-server  name: web-server  namespace: defaultspec:  containers:  - image: 192.168.64.1:5000/nginx:1.21.1    name: web-server    resources: {}  dnsPolicy: ClusterFirst  restartPolicy: Alwaysstatus: {}
复制代码


kubectl apply -f pod-ok.yamlpod/web-server created
复制代码


总结


OPA 哪哪都好,唯一缺点就是其引进的 Rego 语言抬高了使用的门槛。而 Pipy 的规则是通过 JavaScrip 来编写的,前端的同学一样可以完成规则的编写。完全替代可能夸张了一些,但确实在部分场景下可以替代 OPA。


玩到这里,你会发现有了规则,加上功能强大的过滤器(现在我喜欢叫他们 Hook 了),Pipy 的可玩性非常强。


比如OPA: Kubernetes 准入控制策略 Top 5,比如...。大胆的想象吧。

想写一个系列,就叫“如何把 Pipy 玩坏”?


引用链接


[1] GitHub: https://github.com/flomesh-io/pipy

[2] 上一讲功能: https://atbug.com/image-trusted-repository-with-open-policy-agent/

[3] Admission Controller: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

2021-07-15 17:582542

评论

发布
暂无评论
发现更多内容

融云聊天室属性 kv

融云 RongCloud

音视频

【死磕JVM】一道面试题引发的“栈帧”!!!

牧小农

JVM Java虚拟机 运行时数据区 Java虚拟机栈 栈帧

邀请领好礼!米家显示器挂灯、雷蛇烈焰神虫送给你!

滴滴云

寻找被遗忘的勇气(十五)

Changing Lin

3月日更

GitHub Action + ACK:云原生 DevOps 落地利器

阿里巴巴云原生

容器 运维 云原生 k8s 应用服务中间件

Web3.0时代到来 imToken借助区块链帮更多人完成价值存储和转移

CECBC

区块链

华为18级工程师总结的50W字算法、LeetCode、操作系统、计算机底层刷题必备笔记

Java架构之路

Java 程序员 架构 面试 编程语言

互联网信贷风险与大数据 催收管理

张老蔫

28天写作

区块链助飞机加油和支付过程更透明高效

CECBC

区块链

(28DW-S8-Day24) 重新定义流程

mtfelix

28天写作

Redis 在项目中合理使用经验总结

Java小咖秀

redis

使用 Arthas 排查 SpringBoot 诡异耗时的 Bug

阿里巴巴云原生

Java 开发者 云原生 中间件 Arthas

PBAC相对于传统ABAC的优势

龙归科技

IT 架构师 权限 ABAC PBAC

第八章作业

Kalman

产品经理 产品经理训练

第八章学习总结

Kalman

产品经理 产品经理训练

Wireshark 数据包分析学习笔记Day12

穿过生命散发芬芳

Wireshark 数据包分析 3月日更

Redis不是一直号称单线程效率也很高吗,为什么又采用多线程了?

数据库 redis 架构

干货 | 比特币就是时钟

CECBC

比特币 时间同步

Web 安全之 CSRF

架构精进之路

Web 安全 3月日更

「Linux」各目录详解

我是程序员小贱

面试 3月日更

弱网测试工具

Geek_6370d5

如何写好操作类说明文档

lenka

3月日更

面试字节跳动定级2-2,拿32*16offer,P8大佬的算法教程给了我春天!

Java架构之路

Java 程序员 架构 面试 编程语言

HECO火币生态链挖矿系统开发搭建技术

薇電13242772558

数字货币 区块链生态

php 实现单链表以及链表反转等操作

一个大红包

3月日更

学习感悟

shun123456789

架构师训练营第六周作业 - 命题作业

阿德儿

架构师训练营第十周作业 - 命题作业

阿德儿

本科毕业,六年Java开发经验,阿里技术三面+HR面,拿下38*16薪资P7offer

Java架构之路

Java 程序员 架构 面试 编程语言

教你如何在Centos配置Oracle客户端运行时

happlyfox

28天写作 3月日更

openpyxl 对Excel的基础操作

IT蜗壳-Tango

办公自动化 3月日更 IT蜗壳教学

Rego 不好用?用 Pipy 实现 OPA_大数据_InfoQ精选文章