OpenTelemetry(05): Otel Collector Contrib 添加鉴权支持

建议点击 查看原文 查看最新内容。

原文链接: https://typonotes.com/posts/2023/10/07/otel-collector-contrib-with-auth-supportive/

之前我在 OpenTelemetry(1): Golang 接入 OpenTelemetry 完整过程和思路(附源码)- Gin Demo 中提到过, 使用 Otel Collector Contrib 作为中间件 解耦 应用和数据平台。

1. 遇到的困境

此前并没有提到 Otel Collector Contrib 限制接入的问题。

测试的时候在 K8S 集群内部, 服务不对外, 无需鉴权。 但在团队内部推广并上线之后, 就遇到了各种各样的新需求。 最直接的是 Otel 服务要对外暴露到公网

由于没有之前的配置没有鉴权限制, 因此 只要知道服务的地址和端口, 就能给发数据, 这是一个严重的隐患。

2. 解决的方法

在研究了 opentelemetry-collector-contrib 的代码后, 发现其提供多种鉴权方式。

所有支持鉴权方式都在 extension 目录下, 这里列举部分

  1. basic auth extension
  2. bearer token auth extension
  3. oauth2client auth extension
  4. oidc auth extension
  5. 其他 …

每种鉴权方式的配置不一样, 详细的可以参考各自目录下的 README.md 中的内容。

下面说一下 basic 和 token 两种鉴权方式

2.1. Basic Auth Extension

Basic Auth Extension 这种方法就是 http basic 鉴权, 就是我们通常遇到的 账号:密码 鉴权。

2.1.1 服务端配置

支持两种方式将提供 鉴权账号: 文件文本

1
2
3
4
5
6
extensions:
  basicauth/server:
    htpasswd: 
      file: .htpasswd   # 来自文件
      inline: |         # 来自文本
        ${env:BASIC_AUTH_USERNAME}:${env:BASIC_AUTH_PASSWORD} 

Demo 配置如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 添加扩展, 制定 basicauth 扩展的 “名称”, 设置账号密码
extensions:
  basicauth/server:
    htpasswd: 
      inline: |
                ${AUTH_USER}:${AUTH_PASS}

# 2. 在 recivers 中使用 “扩展名称”
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: "0.0.0.0:${GRPC_PORT}"
        auth:
          authenticator: basicauth/server
      http:
        endpoint: "0.0.0.0:${HTTP_PORT}"
        auth:
          authenticator: basicauth/server

# 3. 启用对应名称的 扩展
service:
  extensions: [basicauth/server]

2.1.2. 客户端配置

由于使用的是 http basic 规则, 因此账号密码。

  1. 需要满足使用 冒号 user:pass 连接。
  2. authorization 的值为 Basic base64(value)。 类型为 Basic, 值须要使用 base64 编码。

关于 Authorizaion Header 可以参考扩展文章 API 授权为啥要在 Authorization 标头里加个 Bearer?

以下是代码客户端部分代码提示

1
2
3
4
5
// golang
var (
    UserPass = "auth_user:auth_pass_123"
    base64userpass = fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(UserPass)))
)

在客户端发送请求的时候需要加上 authorizationAuthorization 请求头。 请求头 首字母使用大小写都可以, 服务端有兼容判断。

在客户端创建 Exporter 的时候, 需要使用 WithHeader 方法添加对应的值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// golang

// GRPC demo: Set up a trace exporter
traceExporter, err := otlptracegrpc.New(
    ctx,
    otlptracegrpc.WithGRPCConn(conn),
    otlptracegrpc.WithHeaders(
        map[string]string{
            "authorization": base64userpass,
        },
    ),
)


// HTTP demo: Set up a trace exporter
opts := []otlptracehttp.Option{
    otlptracehttp.WithTimeout(5 * time.Second),
    otlptracehttp.WithEndpoint(endpoint),
    otlptracehttp.WithInsecure(),
    otlptracehttp.WithHeaders(
        map[string]string{
            "authorization": base64userpass,
        },
    ),
}

traceExporter, err := otlptracehttp.New(ctx, opts...)

2.2. Bearer Token Auth Extension

Bearer Token Auth Extension 也是常见的鉴权方法。

注意: BearerTokenAuthExtension 目前(v0.86.0) 只能在 GRCP 协议成功生效。 用于 HTTP 协议的时候始终返回 401 Unauthorized 错误, 经探查 请求数据根本没有到达本扩展

2.2.1. 服务端配置

本扩展支持 默认、自定义 Schema, 以达到兼容目的。

1
2
3
4
5
6
7
extensions:
  bearertokenauth:  # 默认 Schema: Bearer
    token: "somerandomtoken"            # 文本指定
    filename: "file-containing.token"   # 来自文件
  bearertokenauth/withscheme: # 支持用户自定 Schema 
    scheme: "Bearer" 
    token: "randomtoken"

其他配置与 2.1.1. 服务端配置 相似, 不赘述。

2.2.2. 客户端配置

客户端配置与 2.1.2. 客户端配置 相似, 不赘述。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// golang

var (
    BearerAuthToken = "random-token"
    bearertoken     = fmt.Sprintf("Bearer %s", BearerAuthToken)
)

// GRPC: Set up a trace exporter
traceExporter, err := otlptracegrpc.New(
    ctx,
    otlptracegrpc.WithGRPCConn(conn),
    otlptracegrpc.WithHeaders(
        map[string]string{
            "authorization": bearertoken,
        },
    ),
)
// HTTP: Set up a trace exporter
opts := []otlptracehttp.Option{
    otlptracehttp.WithTimeout(5 * time.Second),
    otlptracehttp.WithEndpoint(endpoint),
    otlptracehttp.WithInsecure(),
    otlptracehttp.WithHeaders(
        map[string]string{
            "authorization": bearertoken,
        },
    ),
}