|
| 1 | +--- |
| 2 | +title: "MoE 系列(二)|Golang 扩展从 Envoy 接收配置" |
| 3 | +authorlink: "https://github.com/sofastack" |
| 4 | +description: "MoE 系列(二)|Golang 扩展从 Envoy 接收配置" |
| 5 | +categories: "SOFAStack" |
| 6 | +tags: ["SOFAStack"] |
| 7 | +date: 2023-04-18T15:00:00+08:00 |
| 8 | +cover: "https://mdn.alipayobjects.com/huamei_soxoym/afts/img/A*J10BS4pE_rwAAAAAAAAAAAAADrGAAQ/original" |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +## |
| 13 | + |
| 14 | +文|朱德江(GitHub ID:doujiang24) |
| 15 | + |
| 16 | +MOSN 项目核心开发者 |
| 17 | + |
| 18 | +蚂蚁集团技术专家 |
| 19 | + |
| 20 | + |
| 21 | + |
| 22 | +*专注于云原生网关研发的相关工作* |
| 23 | + |
| 24 | +**本文 1445 字 阅读 5** **分钟** |
| 25 | + |
| 26 | +[*上一篇*](http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247532898&idx=1&sn=26fcd50a6a50666a563bf8feede16959&chksm=faa3aeb8cdd427aeadc7fa02598b2d177e8b2fb239261f917fe4e4ab65ab07bac3851796429f&scene=21#wechat_redirect)我们用一个简单的示例,体验了用 Golang 扩展 Envoy 的极速上手。 |
| 27 | + |
| 28 | +*这次我们再通过一个示例,来体验 Golang 扩展的一个强大的特性:* |
| 29 | + |
| 30 | +***从 Envoy 接收配置***。 |
| 31 | + |
| 32 | +Basic Auth |
| 33 | + |
| 34 | +我们还是从一个小示例来体验,这次我们实现标准的 Basic Auth 的认证,与上一次示例不同的是,这次认证的用户密码信息,需要从 Envoy 传给 Go,不能在 Go 代码中写死了。 |
| 35 | + |
| 36 | +完整的代码可以看 example-basic-auth[1],下面我们展开介绍一番。 |
| 37 | + |
| 38 | +获取配置 |
| 39 | + |
| 40 | +为了更加灵活,在设计上,Envoy 传给 Go 的配置是 Protobuf 的 Any 类型,也就是说,配置内容对于 Envoy 是透明的,我们在 Go 侧注册一个解析器,来完成这个 Any 配置的解析。 |
| 41 | + |
| 42 | +如下示例: |
| 43 | + |
| 44 | +```bash |
| 45 | + |
| 46 | +func init() { |
| 47 | + // 注册 parser |
| 48 | + http.RegisterHttpFilterConfigParser(&parser{}) |
| 49 | +} |
| 50 | + |
| 51 | +func (p *parser) Parse(any *anypb.Any) interface{} { |
| 52 | + configStruct := &xds.TypedStruct{} |
| 53 | + if err := any.UnmarshalTo(configStruct); err != nil { |
| 54 | + panic(err) |
| 55 | + } |
| 56 | + |
| 57 | + v := configStruct.Value |
| 58 | + conf := &config{} |
| 59 | + if username, ok := v.AsMap()["username"].(string); ok { |
| 60 | + conf.username = username |
| 61 | + } |
| 62 | + if password, ok := v.AsMap()["password"].(string); ok { |
| 63 | + conf.password = password |
| 64 | + } |
| 65 | + return conf |
| 66 | +} |
| 67 | + |
| 68 | +``` |
| 69 | +
|
| 70 | +这里为了方便,Any 中的类型是 Envoy 定义的 TypedStruct 类型,这样我们可以直接使用现成的 Go pb 库。 |
| 71 | +
|
| 72 | +值得一提的是,这个配置解析,只有在首次加载的时候需要执行,后续在 Go 使用的是解析后的配置,所以,我们解析到一个 Go map 可以拥有更好的运行时性能。 |
| 73 | +
|
| 74 | +同时,由于 Envoy 的配置,也是有层级关系的,比如 http-filter, virtual host, router, virtual clusters 这四级,我们也支持这四个层级同时有配置,在 Go 侧来组织 merge。 |
| 75 | +
|
| 76 | +当然,这个只有在 Go 侧有复杂的 filter 组织逻辑的时候用得上,后面我们在 MOSN 的上层封装的时候,可以看到这种用法,这里暂时不做展开介绍。 |
| 77 | +
|
| 78 | +认证 |
| 79 | +
|
| 80 | +具体的 Basic Auth 认证逻辑,我们可以参考 Go 标准 net/http 库中的 Basic Auth 实现。 |
| 81 | +
|
| 82 | +```bash |
| 83 | + |
| 84 | +func (f *filter) verify(header api.RequestHeaderMap) (bool, string) { |
| 85 | + auth, ok := header.Get("authorization") |
| 86 | + if !ok { |
| 87 | + return false, "no Authorization" |
| 88 | + } |
| 89 | + username, password, ok := parseBasicAuth(auth) |
| 90 | + if !ok { |
| 91 | + return false, "invalid Authorization format" |
| 92 | + } |
| 93 | + if f.config.username == username && f.config.password == password { |
| 94 | + return true, "" |
| 95 | + } |
| 96 | + return false, "invalid username or password" |
| 97 | +} |
| 98 | + |
| 99 | +``` |
| 100 | +
|
| 101 | +这里面的 `parseBasicAuth` 就是从 net/http 库中的实现,是不是很方便呢。 |
| 102 | +
|
| 103 | +配置 |
| 104 | +
|
| 105 | +简单起见,这次我们使用本地文件的配置方式。如下是关键的配置: |
| 106 | +
|
| 107 | +```bash |
| 108 | + |
| 109 | +http_filters: |
| 110 | + - name: envoy.filters.http.golang |
| 111 | + typed_config: |
| 112 | + "@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config |
| 113 | + library_id: example |
| 114 | + library_path: /etc/envoy/libgolang.so |
| 115 | + plugin_name: basic-auth |
| 116 | + plugin_config: |
| 117 | + "@type": type.googleapis.com/xds.type.v3.TypedStruct |
| 118 | + value: |
| 119 | + username: "foo" |
| 120 | + password: "bar" |
| 121 | + |
| 122 | +``` |
| 123 | +
|
| 124 | +这里我们配置了用户名密码:`foo:bar`。 |
| 125 | +
|
| 126 | +预告一下,下一篇我们会体验通过 Istio 来推送配置,体会一番动态更新配置的全流程。 |
| 127 | +
|
| 128 | +测试 |
| 129 | +
|
| 130 | +编译,运行,跟上篇一样,我们还是使用 Envoy 官方提供的镜像即可。 |
| 131 | +
|
| 132 | +跑起来之后,我们测试一下: |
| 133 | +
|
| 134 | +```bash |
| 135 | + |
| 136 | +$ curl -s -I 'http://localhost:10000/' |
| 137 | +HTTP/1.1 401 Unauthorized |
| 138 | + |
| 139 | +# invalid username:password |
| 140 | +$ curl -s -I 'http://localhost:10000/' -H 'Authorization: basic invalid' |
| 141 | +HTTP/1.1 401 Unauthorized |
| 142 | + |
| 143 | +# valid foo:bar |
| 144 | +$ curl -s -I 'http://localhost:10000/' -H 'Authorization: basic Zm9vOmJhcg==' |
| 145 | +HTTP/1.1 200 OK |
| 146 | + |
| 147 | +``` |
| 148 | +
|
| 149 | +是不是很简单呢,一个标准的 Basic Auth 扩展就完成了。 |
| 150 | +
|
| 151 | +总结 |
| 152 | +
|
| 153 | +Envoy 是面向云原生的架构设计,提供了配置动态变更的机制,Go 扩展可以从 Envoy 接受配置,也就意味着 Go 扩展也可以很好的利用这套机制。 |
| 154 | +
|
| 155 | +Go 扩展的开发者,不需要关心配置的动态更新,只需要解析配置即可,非常的方便~ |
| 156 | +
|
| 157 | +下一篇我们会介绍,配合 Istio 来动态更新用户名密码,体验一番云原生的配置变更体验。 |
| 158 | +
|
| 159 | +后续还有更多 Golang 扩展的特性介绍,原理解析,以及,更上层的 MOSN 集成体验,欢迎持续关注。 |
| 160 | +
|
| 161 | +[1]example-basic-auth: |
| 162 | +
|
| 163 | +*[https://github.com/doujiang24/envoy-go-filter-example/tree/master/example-basic-auth](https://github.com/doujiang24/envoy-go-filter-example/tree/master/example-basic-auth)* |
| 164 | +
|
| 165 | +**了解更多…** |
| 166 | +
|
| 167 | +**MOSN Star 一下✨:** |
| 168 | +[*https://github.com/mosn/mosn*](https://github.com/mosn/mosn) |
0 commit comments