ASM通过VirtualService API提供丰富的路由能力和请求操纵能力,通过AuthorizationPolicy API提供丰富的安全能力,但是在一些特定场景下,这些API可能仍然不能满足需求。您可以通过编写自定义的Wasm Filter并添加到ASM网关或Sidecar来实现复杂场景的自定义行为。本文介绍如何通过WasmPlugin API为ASM网关启用Coraza WAF。
前提条件
已添加集群到ASM实例,且ASM实例版本为1.18及以上。
已启用Sidecar注入。具体操作,请参见配置Sidecar注入策略。
已部署httpbin应用,且可以正常访问。具体操作,请参见部署httpbin应用。
已创建阿里云容器镜像服务企业版(企业版实例支持OCI镜像)。具体操作,请参见创建企业版实例。
已安装Docker,请参见:Get Docker
步骤一:制作Wasm Plugin OCI镜像并推送至阿里云容器镜像服务
Wasm Plugin有以下三种加载方式。本文以第二种方式为例,制作Wasm Plugin的OCI镜像,并将其推送到阿里云容器镜像服务。
将Wasm扩展存储至configmap,并Mount至容器,ASM网格代理或ASM网关从本地文件系统加载。
将Wasm扩展制作成OCI镜像上传至镜像仓库,ASM网格代理或ASM网关从镜像仓库拉取。
将Wasm扩展上传至云存储或其他支持HTTP协议下载的服务,ASM网格代理或网关通过网络下载。
下载Coraza Wasm插件并构建镜像。
执行以下命令,下载Coraza Wasm插件至本地,并解压缩。
wget https://github.com/corazawaf/coraza-proxy-wasm/releases/download/0.3.0/coraza-proxy-wasm-0.3.0.zip unzip coraza-proxy-wasm-0.3.0.zip
执行以下命令,为Coraza Wasm插件重命名。
上一步解压完成后,会得到coraza-proxy-wasm.wasm文件。ASM要求Wasm插件必须命名为plugin.wasm,因此需要重命名。
mv coraza-proxy-wasm.wasm plugin.wasm
在当前目录下创建Dockerfile文件,内容如下:
FROM scratch ADD ./plugin.wasm ./plugin.wasm
执行如下命令构建镜像:
docker build -t coraza-proxy-wasm:latest .
推送镜像到ACR企业版实例。
创建命名空间。
登录容器镜像服务控制台,在左侧导航栏,单击实例列表。
在实例列表页面,单击目标企业版实例。
在左侧导航栏,单击
,然后单击创建命名空间,配置相关信息,然后单击确定。本示例配置命名空间名称为wasm。
在左侧导航栏,单击
,单击公网页签,打开访问入口开关,按需添加公网白名单。如果不需要白名单控制,请删除默认的白名单。
说明本文采用公网地址作为演示。在实际应用中,您可以配置专有网络访问以实现更高的私密性和更好的网络性能。更多信息,请参见配置专有网络的访问控制。
执行以下命令,登录ACR企业版实例,输入密码。
docker login --username=登录用户名 enterprise-registry.cn-hangzhou.cr.aliyuncs.com
执行以下命令,将镜像推送到镜像仓库。
docker tag ${ImageId} enterprise-registry.cn-hangzhou.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latest docker push enterprise-registry.cn-hangzhou.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latest
验证镜像是否推送成功。
登录容器镜像服务控制台,在左侧导航栏,单击实例列表。
在实例列表页面,单击目标企业版实例。
在左侧导航栏,单击
,单击coraza-proxy-wasm,然后在左侧导航栏,单击镜像版本。若显示刚推送的标签为
latest
的镜像,表明镜像推送成功。
步骤二:配置镜像拉取权限
创建私有仓库后,您需要使用容器镜像服务配置的口令创建Secret。ASM网格代理或ASM网关将使用Secret中的授权信息在拉取镜像时进行认证。
执行以下命令,创建Secret。
kubectl create secret docker-registry -n istio-system coraza-wasm-proxy --docker-server=enterprise-registry.cn-hangzhou.cr.aliyuncs.com --docker-username=用户名 --docker-password=密码
Secret需要保持与Wasm Plugin处于同一命名空间。本文的Wasm Plugin命名空间为istio-system,因此对应的Secret在istio-system命名空间。在实际使用中,请您根据实际情况调整以上命令中
-n
参数后的命名空间名称。执行以下命令,检查Secret是否创建成功。
kubectl -n isito-system get secret coraza-wasm-proxy
步骤三:应用WasmPlugin API到ASM
WasmPlugin API用于声明在目标网关或网格代理上使用的插件及相关配置。
使用以下内容,创建wasm-plugin.yaml。
apiVersion: extensions.istio.io/v1alpha1 kind: WasmPlugin metadata: name: coraza-proxy-wasm namespace: istio-system spec: imagePullPolicy: IfNotPresent imagePullSecret: coraza-wasm-proxy selector: matchLabels: istio: ingressgateway url: oci://enterprise-registry.cn-hangzhou.cr.aliyuncs.com/wasm/coraza-proxy-wasm:latest phase: AUTHN pluginConfig: directives_map: default: - "SecDebugLogLevel 9" - "SecRuleEngine On" - "SecRule REQUEST_HEADERS:x-user-type \"@streq baned\" \"id:101,phase:1,t:lowercase,deny,msg:'denied by header'\"" default_directives: default
字段
说明
spec.url
指定OCI镜像的地址和Tag。
spec.imagePullSecret
指定用于进行镜像仓库鉴权的Secret。
spec.selector
指定生效的工作负载。
spec.phase
指定插件生效的阶段。
sepc.pluginConfig
指定插件配置。
directives_map
:定义名为default
的directive
,其中规则指定当请求携带x-user-type
Header,且值为baned
时,拒绝该请求。default_directives
:指定默认的directive
为default
。
在ASM实例对应的KubeConfig环境下,执行以下命令,将WasmPlugin应用到ASM实例。
kubectl apply -f wasm-plugin.yaml
步骤四:验证WasmPlugin API是否生效
执行以下命令,访问httpbin应用。
请将
http://120.27.XXX.XX/
替换为实际的网关地址。关于如何获取网关地址,请参见获取入口网关地址。curl -v http://120.27.XXX.XX/
预期输出:
* Trying 120.27.XXX.XX:80... * Connected to 120.27.XXX.XX (120.27.XXX.XX) port 80 (#0) > GET / HTTP/1.1 > Host: 120.27.XXX.XX > User-Agent: curl/7.86.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < server: istio-envoy < date: Fri, 27 Oct 2023 03:21:19 GMT < content-type: text/html; charset=utf-8 < content-length: 9593 < access-control-allow-origin: * < access-control-allow-credentials: true < x-envoy-upstream-service-time: 2 ......
可以看到请求返回
200 OK
,符合预期。执行以下命令,为请求添加
header:x-user-type: baned
,再次访问httpbin应用。请将
http://120.27.XXX.XX/
替换为实际的网关地址。curl -v -H 'x-user-type: baned' http://120.27.XXX.XX/
预期输出:
* Trying 120.27.XXX.XX:80... * Connected to 120.27.XXX.XX (120.27.XXX.XX) port 80 (#0) > GET / HTTP/1.1 > Host: 120.27.XXX.XX > User-Agent: curl/7.86.0 > Accept: */* > x-user-type: baned > * Mark bundle as not supporting multiuse < HTTP/1.1 403 Forbidden < date: Fri, 27 Oct 2023 03:22:19 GMT < server: istio-envoy < content-length: 0 < * Connection #0 to host 120.27.XXX.XX left intact
可以看到请求返回
403 Forbidden
,被网关拒绝,符合预期。