SEANK.H.LIAO

opentelemetry collector oidc auth

OpenID Connect authentication with the OpenTelemetry Collector

otelcol oidc auth

The OpenTelemetry Collector (otelcol) has an oidcauth extension. This makes for an interesting alternative to using:

oidc token

First, we're going to need an OIDC identity token. I'm going to use GCP because... that's what I have (and where my workloads are running). gcloud has a command to print out an identity token for a service account, note this requires roles/iam.serviceAccountTokenCreator on the account itself, and maybe switching over to it for default auth?

1gcloud auth print-identity-token \
2  hwaryun@com-seankhliao.iam.gserviceaccount.com \
3  --include-email \
4  --audiences otelcol \
5  --impersonate-service-account hwaryun@com-seankhliao.iam.gserviceaccount.com

This gives you a long token like (------ parts will be actual chars):

1eyJhbGci------------J0eXAiOiJKV1QifQ.eyJhdWQi---------xODYifQ.FfpWwTlS24zl------v3pRd33tZmQgq1gLabQ

collector

On the collector side we'll nned the oidcauth extension configured for Google's accounts endpoint, and with the right header extractor.

The verified data is stored in the context's authdata which can be extracted with the processors into proper attributes, something you'll want to do before reshaping/regrouping data with batch/groupbyX. Also, as an attribute, it's where you can implement richer filtering (eg checking project via email).

 1extensions:
 2  # due to header canonicaliztion weirdness
 3  # http1 uses "Authorization" (leading capital)
 4  # while grpc uses lowercase
 5  oidc/grpc:
 6    issuer_url: https://accounts.google.com
 7    audience: otelcol
 8    username_claim: email
 9
10  oidc/http:
11    issuer_url: https://accounts.google.com
12    audience: otelcol
13    username_claim: email
14    attribute: Authorization
15
16receivers:
17  otlp:
18    protocols:
19      http:
20        auth:
21          authenticator: oidc/http
22      grpc:
23        auth:
24          authenticator: oidc/grpc
25
26processors:
27  # using th resource processor, we can pull out auth data from the auth. * context
28  # also available for the attribute processor
29  # if we want to do any stricter validation, it can be done in a processor
30  # eg, only allowing users with our @project.iam.gserviceaccount.com emails
31  resource:
32    attributes:
33      - key: oidc.subject
34        action: upsert
35        from_context: auth.subject
36      # google service accounts don't set this field
37      - key: oidc.membership
38        action: upsert
39        from_context: auth.membership
40
41exporters:
42  logging:
43    verbosity: detailed
44
45service:
46  extensions: [oidc/http, oidc/grpc]
47  pipelines:
48    traces:
49      receivers: [otlp]
50      processors: [resource]
51      exporters: [logging]
52  telemetry:
53    logs:
54      level: debug

client

Now we need a client to use the token

curl

If we only care about testing if the token is accepted, curl works.

note: this uses the otlp http endpoint.

1curl -v http://localhost:4318/ -H "authorization: Bearer ${ID_TOKEN}"
tracegen

tracegen is a tool that is part of the opentelemetry-collector-contrib project. It's an easy way to generate valid data.

note: the quoting rules as tracegen requires the value be quoted

1tracegen -otlp-insecure -otlp-header 'authorization="Bearer '${ID_TOKEN}'"'
go

For Go, this just mean pulling in GCP's idtoken generator and hooking that in to gRPC. It should be easy to swap out to any other token provider.

 1package main
 2
 3import (
 4        "context"
 5        "crypto/tls"
 6        "log"
 7
 8        "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
 9        "google.golang.org/api/idtoken"
10        "google.golang.org/grpc"
11        "google.golang.org/grpc/credentials"
12        "google.golang.org/grpc/credentials/oauth"
13)
14
15func main() {
16        ctx := context.Background()
17        gcpTS, err := idtoken.NewTokenSource(ctx, "otelcol")
18        if err != nil {
19                log.Fatal(err)
20        }
21
22        exporter, err := otlptracegrpc.New(ctx,
23                otlptracegrpc.WithDialOption(
24                        // TLS required for credentials
25                        grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})),
26                        // id token per request in headers
27                        grpc.WithPerRPCCredentials(&oauth.TokenSource{TokenSource: gcpTS}),
28                ),
29        )
30        if err != nil {
31                log.Fatalln(err)
32        }
33}
34