Files
o11y/cmd/community/identity.go
zeekayandHanzo Dev 212410de59 The warehouse is reached as an IAM identity, never a password
A telemetry store now presents an IAM access token minted for the
datastore audience at every handshake, and has no other credential to
present. The token is the embedding process's identity, set by the host
rather than read from configuration: the standalone server mints its own
from IAM_CLIENT_ID and IAM_CLIENT_SECRET, and cloud hands the embed the
identity its o11y app already holds.

A DSN now says where the warehouse is and nothing else. One that names a
user or password is refused, and a store with no identity is not built.
Pooled connections live five minutes, so each replacement presents a
current token and a revoked identity keeps nothing for long.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 14:35:36 -07:00

62 lines
2.1 KiB
Go

package main
import (
"context"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"time"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)
// warehouse is the audience a warehouse token is minted for (RFC 8707). The
// warehouse admits a token for this audience and nothing else.
const warehouse = "datastore"
// renewLead is how long before expiry a token is replaced: it is presented at a
// handshake the connection then outlives, so one about to lapse is never handed
// out.
const renewLead = 2 * time.Minute
// identity is this server's own IAM identity for the warehouse: the
// client_credentials grant for IAM_CLIENT_ID / IAM_CLIENT_SECRET at IAM_URL,
// scoped to the datastore audience, cached and renewed ahead of expiry. The
// server holds no warehouse credential of any other kind, so without an identity
// it does not start.
func identity() (func(context.Context) (string, error), error) {
id := strings.TrimSpace(os.Getenv("IAM_CLIENT_ID"))
secret := os.Getenv("IAM_CLIENT_SECRET")
base := strings.TrimRight(strings.TrimSpace(os.Getenv("IAM_URL")), "/")
if id == "" || secret == "" || base == "" {
return nil, fmt.Errorf("the warehouse admits an IAM identity and nothing else: set IAM_URL, IAM_CLIENT_ID and IAM_CLIENT_SECRET")
}
cc := &clientcredentials.Config{
ClientID: id,
ClientSecret: secret,
TokenURL: base + "/v1/iam/oauth/token",
AuthStyle: oauth2.AuthStyleInParams,
EndpointParams: url.Values{"resource": {warehouse}},
}
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{Timeout: 30 * time.Second})
src := oauth2.ReuseTokenSourceWithExpiry(nil, mint{cc: cc, ctx: ctx}, renewLead)
return func(context.Context) (string, error) {
t, err := src.Token()
if err != nil {
return "", fmt.Errorf("warehouse identity: %w", err)
}
return t.AccessToken, nil
}, nil
}
// mint asks IAM for a fresh token every call; the one cache is identity's.
type mint struct {
cc *clientcredentials.Config
ctx context.Context
}
func (m mint) Token() (*oauth2.Token, error) { return m.cc.Token(m.ctx) }