当前位置: 首页 > news >正文

增强 HTNN 服务网格功能:基于 Istio 的BasicAuth 与 ACL 插件开发实战

目录

1.引言

什么是HTNN?

为什么开发 BasicAuth 和 ACL 插件?

2.技术背景

技术栈概览

Istio 与服务网格简述

HTNN 框架与插件机制概览

3.插件开发详解:BasicAuth 与 ACL

3.1 BasicAuth插件

功能点

实现细节

3.2 ACL插件

功能点

实现细节

4.实践过程

4.1本地构建自定义HTNN镜像

1.修改源码并添加插件

2.构建Docker镜像

4.2 在 Kubernetes 中部署插件

1.将构建好的数据面和控制面的镜像先导入到Kubernetes集群

2.修改Helm Chart的 values.yaml文件

4.3 测试验证流程


1.引言

什么是HTNN?

HTNN 是一个基于 Istio 构建的服务网格增强项目,通过插件机制来扩展和定制流量控制、安全策略等能力。与 Istio 原生的 Mixer 插件体系不同,HTNN 提供了更加灵活和轻量的插件注册与运行方式,适合快速验证和迭代。

项目地址:https://github.com/mosn/htnn

为什么开发 BasicAuth 和 ACL 插件?

在 HTNN 官方仓库中,虽然提供了如 key-authhmac-auth 等认证插件,但对于传统的用户名密码认证(BasicAuth)以及基于源 IP 的访问控制(ACL)仍未支持。在一些面向公网服务或细粒度控制场景中,这类插件是非常常用且必要的。

因此,我基于 HTNN 插件机制,自行开发并集成了:

basicAuth 插件:用于支持 HTTP Basic Authentication,适用于快速接入第三方平台或内部服务接口保护。

acl 插件:实现对来源 IP 的白名单与黑名单策略配置,增强服务边界的访问控制能力。

2.技术背景

技术栈概览

技术作用
Go编写插件逻辑,HTNN 本身使用 Go 开发
Protobuf定义插件配置结构、实现插件与框架间的序列化通信
Docker构建包含自定义插件的 HTNN 镜像
Kubernetes部署测试 HTNN 与插件,验证实际运行效果
Linux本地开发与测试环境,使用 shell 工具辅助调试
Istio服务网格的核心组件,HTNN 作为其增强层构建在上方

Istio 与服务网格简述

Istio 是当前主流的服务网格框架,提供流量管理、安全控制、可观测性等能力。在微服务架构中,Istio 通过 sidecar(通常是 Envoy)注入方式拦截进出流量,实现服务间的无侵入治理。然而,Istio 自身对于某些认证或访问控制的定制化支持较为复杂或不够灵活,这正是 HTNN 出现的背景。

HTNN 框架与插件机制概览

热插拔机制:插件通过 plugins.RegisterPlugin 接口注册,支持动态加载与配置。

统一请求拦截点:插件可以在请求进入 Envoy 之前对其进行处理(如验证、拦截)。

可配置结构:通过 Protobuf 配置文件或 ConfigMap 传递运行参数,实现运行时灵活控制。

与 Istio 无缝集成:在 Istio 的 service mesh 中以 sidecar 的形式运行,兼容 Istio 原生组件。

3.插件开发详解:BasicAuth 与 ACL

3.1 BasicAuth插件

功能点

1.基于 HTTP Basic Auth 的认证:

验证 HTTP 请求中的 Authorization 头部,确保用户提供了正确的用户名和密码。

如果认证失败,返回 HTTP 401 状态码。

2.支持多用户凭据:

插件支持配置多个用户名和密码对,允许不同用户访问。

3.动态配置:

用户名和密码通过配置文件动态加载,支持灵活调整

4.安全性:

使用 Base64 解码解析 Authorization 头部中的凭据。

如果凭据无效或缺失,拒绝请求。

实现细节

1. 配置结构

配置文件定义在 [config.go] 中:

type Config struct {Credentials map[string]string `json:"credentials,omitempty"`
}

Credentials 字段

键是用户名,值是对应的密码。

示例配置:

{"credentials": {"user1": "password1","user2": "password2"}
}

2. 插件注册

插件通过 plugins.RegisterPlugin 注册到框架中:

func init() {plugins.RegisterPlugin(basicauth.Name, &plugin{})
}

3. 核心逻辑

核心逻辑实现于 [filter.go] 中:

func (f *filter) DecodeHeaders(headers api.RequestHeaderMap, endStream bool) api.ResultAction {authHeader, ok := headers.Get("Authorization")if !ok || !strings.HasPrefix(authHeader, "Basic ") {return &api.LocalResponse{Code: 401, Msg: "missing or invalid Authorization header"}}encodedCredentials := strings.TrimPrefix(authHeader, "Basic ")decoded, err := base64.StdEncoding.DecodeString(encodedCredentials)if err != nil {return &api.LocalResponse{Code: 401, Msg: "invalid Authorization header"}}parts := strings.SplitN(string(decoded), ":", 2)if len(parts) != 2 {return &api.LocalResponse{Code: 401, Msg: "invalid Authorization header"}}username, password := parts[0], parts[1]if validPassword, ok := f.config.Credentials[username]; !ok || validPassword != password {return &api.LocalResponse{Code: 401, Msg: "invalid username or password"}}return api.Continue
}

逻辑说明:

检查 Authorization 头部是否存在,且是否以 Basic  开头。

使用 Base64 解码凭据,并解析出用户名和密码。

验证用户名和密码是否匹配配置中的凭据。

如果验证失败,返回 HTTP 401;否则继续处理请求。

4. 单元测试

单元测试位于 [filter_test.go]:

func TestBasicAuthFilter(t *testing.T) {tests := []struct {name     stringconf     stringheaders  map[string][]stringexpected int}{{name: "valid credentials",conf: `{"credentials": {"user1": "password1","user2": "password2"}}`,headers: map[string][]string{"Authorization": {"Basic " + base64.StdEncoding.EncodeToString([]byte("user1:password1"))},},expected: 0,},{name: "invalid credentials",conf: `{"credentials": {"user1": "password1","user2": "password2"}}`,headers: map[string][]string{"Authorization": {"Basic " + base64.StdEncoding.EncodeToString([]byte("user1:wrongpassword"))},},expected: 401,},}
}

测试覆盖了以下场景:

有效的用户名和密码。

无效的用户名或密码。

缺失 Authorization 头部

3.2 ACL插件

功能点

1.基于IP地址的访问控制:

支持通过 allow_list 和 deny_list 配置访问规则。

优先检查 deny_list,如果匹配则拒绝访问。

2.CIDR支持:

支持 CIDR 格式的 IP 地址范围匹配。

3.动态配置:

通过配置文件动态加载访问控制规则。

4.安全性:

如果请求的 IP 地址不在 allow_list 中,默认拒绝访问。

实现细节

1.配置结构

配置文件定义在 [config.go] 中:

type Config struct {AllowList []string `json:"allow_list,omitempty"`DenyList  []string `json:"deny_list,omitempty"`
}

AllowList 和 DenyList 字段

AllowList:允许访问的 IP 地址或 CIDR 范围。

DenyList:拒绝访问的 IP 地址或 CIDR 范围。

示例配置:

{"allow_list": ["192.168.1.0/24"],"deny_list": ["10.0.0.1"]
}

2.插件注册

插件通过 plugins.RegisterPlugin 注册到框架中:

func init() {plugins.RegisterPlugin(acl.Name, &plugin{})
}

3.核心逻辑

核心逻辑实现于 [filter.go] 中:

func (f *filter) DecodeHeaders(headers api.RequestHeaderMap, endStream bool) api.ResultAction {clientIP, ok := headers.Get("X-Forwarded-For")if !ok {return &api.LocalResponse{Code: 403, Msg: "client IP not found"}}for _, denyIP := range f.config.DenyList {if isIPMatch(clientIP, denyIP) {return &api.LocalResponse{Code: 403, Msg: "access denied"}}}for _, allowIP := range f.config.AllowList {if isIPMatch(clientIP, allowIP) {return api.Continue}}return &api.LocalResponse{Code: 403, Msg: "access denied"}
}

逻辑说明:

获取请求头中的 X-Forwarded-For 字段,提取客户端 IP。

检查 IP 是否匹配 deny_list,如果匹配则拒绝访问。

检查 IP 是否匹配 allow_list,如果匹配则允许访问。

如果未匹配任何规则,默认拒绝访问。

4.单元测试

单元测试位于 [filter_test.go]:

func TestACLFilter(t *testing.T) {tests := []struct {name     stringconf     stringheaders  map[string][]stringexpected int}{{name: "deny list match",conf: `{"allow_list": ["192.168.1.0/24"],"deny_list": ["10.0.0.1"]}`,headers: map[string][]string{"X-Forwarded-For": {"10.0.0.1"},},expected: 403,},{name: "allow list match",conf: `{"allow_list": ["192.168.1.0/24"],"deny_list": ["10.0.0.1"]}`,headers: map[string][]string{"X-Forwarded-For": {"192.168.1.50"},},expected: 0,},}
}

测试覆盖了以下场景

匹配 deny_list

匹配 allow_list

未匹配任何规则。

4.实践过程

4.1本地构建自定义HTNN镜像

1.修改源码并添加插件

分别在types\plugins(实现配置)和plugins\plugins(实现逻辑)目录下新增basicauth和acl目录。

分别在两个目录下的主插件入口调用RegisterPlugin(...)注册。

2.构建Docker镜像

先检查实现.editorconfig文件的代码格式规范,避免因系统版本不兼容导致换行符等问题影响镜像的构建

dockerfile文件直接复用 htnn\manifests\images\cp\Dockerfile 和htnn\manifests\images\dp\Dockerfile 就行

cd到 htnn\manifests\Makefile 目录下执行 make 命令完成构建:

make build-proxy-imagemake build-controller-image

4.2 在 Kubernetes 中部署插件

1.将构建好的数据面和控制面的镜像先导入到Kubernetes集群
minikube image load your-image-name1:tagminikube image load your-image-name2:tag
2.修改Helm Chart的 values.yaml文件
#文件 htnn\manifests\charts\htnn-controller\values.yaml
hub: ""image: htnn-controllertag: "latest"imagePullPolicy: "Never" # 不使用远程镜像
#文件 manifests\charts\htnn-gateway\values.yaml
gateway:name: istio-ingressgatewayimage: m.daocloud.io/docker.io/envoyproxy/envoy:latestimagePullPolicy: Neverenv:ISTIO_DELTA_XDS: "true"
#默认的manifests\charts\htnn-gateway\values.schema.json文件中没有镜像参数
#需要在json文件中添加该结构参数

3.部署或更新 HTNN 到 Kubernetes

首次部署:

控制面

helm install htnn-controller 本地Path\htnn\manifests\charts\htnn-controller --namespace istio-system --create-namespace -f 本地Path\htnn\manifests\charts\htnn-controller\values.yaml

 数据面

helm install htnn-gateway 本地Path\htnn\manifests\charts\htnn-gateway -n istio-system -f 本地Path\htnn\manifests\charts\htnn-gateway\values.yaml 

已有部署则使用升级命令 upgrade 替换 install

4.查看 istio-system 命名空间下所有 Pod 的运行状态

PS C:\WINDOWS\system32> kubectl -n istio-system get pods
NAME                                    READY   STATUS    RESTARTS       AGE
istio-ingressgateway-674cd8d4c9-lg692   1/1     Running   3 (3d2h ago)   3d20h
istiod-6b6c464bb7-2df62                 1/1     Running   3 (3d2h ago)   3d20h

如果出现异常状态,用以下命令查看原因

kubectl -n istio-system describe pod <pod-name>

4.3 测试验证流程

分别进行后端服务部署、入口流量管理、路由规则定义和安全认证策略配置,配置到kubernetes环境中

部署后端服务

//backend.yaml
---
apiVersion: v1
kind: Service
metadata:name: backendnamespace: istio-systemlabels:app: backend
spec:ports:- port: 80name: httptargetPort: 5678selector:app: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:name: backendnamespace: istio-system
spec:replicas: 1selector:matchLabels:app: backendtemplate:metadata:labels:app: backendspec:containers:- name: backendimage: hashicorp/http-echoargs:- "-text=hello from backend"ports:- containerPort: 5678

 定义入口网关

//gateway.yaml
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:name: htnn-gatewaynamespace: istio-system
spec:selector:istio: ingressgatewayservers:- port:number: 80name: httpprotocol: HTTPhosts:- "*"

 定义虚拟服务路由规则

//vs-basicauth.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:name: vs-basicauthnamespace: istio-system
spec:hosts:- "*"gateways:- htnn-gatewayhttp:- match:- uri:prefix: /route:- destination:host: backend.istio-system.svc.cluster.localport:number: 80

 定义 basicAuth 插件的策略

//basicauthpolicy.yaml
apiVersion: htnn.mosn.io/v1
kind: FilterPolicy
metadata:name: basicauth-policynamespace: istio-system
spec:targetRef:group: networking.istio.iokind: VirtualServicename: vs-basicauthfilters:basicAuth:config:credentials:admin: "admin123"user: "user123"

先通过 status 检查下策略是否被接受:

$ kubectl -n istio-system get filterpolicies.htnn.mosn.io policy -o yaml···​
status:conditions:- lastTransitionTime: "2025-05-15T13:56:42Z"message: The policy has been acceptedobservedGeneration: 1reason: Acceptedstatus: "True"type: Accepted
kind: List
metadata:resourceVersion: ""

让我们在一个终端上执行 port-forward,让本地的客户端可以访问到 k8s 里面的服务:

kubectl port-forward -n istio-system svc/istio-ingressgateway 30474:80

在另一个终端上,我们可以通过 30474 端口访问到 HTNN:

//正确的用户名和密码
curl -v --user admin:admin123 http://localhost:30474/get
HTTP/1.1 200 OK
//不正确的
curl -v --user admin:admin121 http://localhost:30474/get
HTTP/1.1 401 Unauthorized

以上是basicauth插件的测试流程,acl的流程是一样的,先将配置文件写好,再应用策略:

//aclpolicy.yaml
apiVersion: htnn.mosn.io/v1
kind: FilterPolicy
metadata:name: acl-policynamespace: istio-system
spec:targetRef:group: networking.istio.iokind: VirtualServicename: vs-aclfilters:acl:config:allow_list:- "192.168.1.0/24"deny_list:- "10.0.0.1"

 让我们在一个终端上执行 port-forward,让本地的客户端可以访问到 k8s 里面的服务:

kubectl port-forward -n istio-system svc/istio-ingressgateway 30474:80

在另一个终端上,我们可以通过 30474 端口访问到 HTNN:

curl -v --header "Host: backend.local" --header "X-Forwarded-For: 192.168.1.100" http://localhost:30474/getHTTP/1.1 200 OKcurl -v --header "Host: backend.local" --header "X-Forwarded-For: 10.0.0.1" http://localhost:30474/getHTTP/1.1 403 Forbidden

http://www.dtcms.com/a/192562.html

相关文章:

  • 【js】JavaScript的变量提升、函数声明提升
  • 知识图谱系列(2):知识图谱的技术架构与组成要素
  • 【补充笔记】修复“NameError: name ‘ZhNormalizer‘ is not defined”的直接方法
  • Kafka如何实现高性能
  • Unity碰撞检测:射线与胶囊体投射/Layer(层)、LayerMask(遮罩层)
  • Unity3D开发AI桌面精灵/宠物系列 【六】 人物模型 语音口型同步 LipSync 、梅尔频谱MFCC技术、支持中英文自定义编辑- 基于 C# 语言开发
  • Linux云计算训练营笔记day08(MySQL数据库)
  • 【上位机——WPF】Window标签常用属性
  • 【学习心得】2025年Docker Desktop安装记录
  • 阿里云ECS部署Dify
  • 阿里云CMH镜像迁移与SMC整机迁移对比及功能详解(同地域跨主体账号场景)
  • 配置VScodePython环境Python was not found;
  • 「Java EE开发指南」如何使用MyEclipse的可视化JSF编辑器设计JSP?(二)
  • PC:使用WinSCP密钥文件连接sftp服务器
  • ANTsPy:医学影像处理python库
  • Java集合详解:LinkedBlockingQueue
  • 26考研 | 王道 | 计算机组成原理 | 一、计算机系统概述
  • Window下Jmeter多机压测方法
  • 128.在 Vue 3 中使用 OpenLayers 实现绘制矩形截图并保存地图区域
  • OpenShift AI - 用 ModelCar 构建容器化模型,提升模型弹性扩展速度
  • IP地址、端口、TCP介绍、socket介绍、程序中socket管理
  • Golang 设计哲学
  • 用Python代码绘制动态3D爱心效果
  • AI日报 · 2025年5月15日|GPT-4.1 登陆 ChatGPT
  • 实验-时序电路设计2-存储器阵列(数字逻辑)
  • 光谱相机的图像预处理技术
  • MYSQL基本命令
  • 70、微服务保姆教程(十三)Docker容器详细讲义
  • 人体肢体渲染-一步几个脚印从头设计数字生命——仙盟创梦IDE
  • 工业操作系统核心技术揭秘