Tokensmith is an Envoy external authorizer (ext_authz) for Istio 1.27+ that exchanges OIDC ID tokens for Kubernetes service accounts in one cluster for ID tokens for valid Kubernetes service accounts in another cluster.
In multi-cluster Kubernetes environments, services often need to authenticate across cluster boundaries. Tokensmith enables secure cross-cluster authentication by:
- Accepting requests from Envoy with bearer tokens from a workload cluster
- Validating the incoming token using Kubernetes TokenReview API
- Exchanging the validated token for a management cluster token using TokenRequest API
- Returning the new token to Envoy for use in downstream API server requests
This allows services in one Kubernetes cluster (workload) to securely access resources in another cluster (management) using their native Kubernetes service account identities.
Tokensmith implements the Envoy External Authorization API and acts as a token translation layer between clusters:
- External Authorization Server: gRPC server implementing Envoy's ext_authz API
- Token Validator: Validates tokens from workload cluster using Kubernetes TokenReview API
- Token Exchanger: Creates new tokens for management cluster using Kubernetes TokenRequest API
- Identity Mapping: Maps workload service accounts to identical namespace/name in management cluster
✅ Core Implementation Complete - See plan/004-oidc-token-exchange-overview.md for implementation details.
- Go 1.23 or later
- Make
- golangci-lint (for linting)
# Build the binary
make build
# Run tests
make test
# Run linters
make lint
# Format code
make fmtRun all unit tests:
go test ./...Run tests for a specific package with verbose output:
go test -v ./internal/token/Run a specific test:
go test -v ./internal/token/ -run TestTokenExchangeFlowGenerate test coverage report:
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.outView coverage for a specific package:
go test -cover ./internal/token/The token exchange tests (internal/token/exchange_integration_test.go) use Kubernetes fake clients and generate real JWT tokens to test the complete validation and exchange flow:
- TestTokenExchangeFlow: Validates the happy path, verifying that JWT claims (subject, audience, expiration, issuer, namespace, service account name) are preserved during exchange, while unique identifiers (JWT ID, service account UID) differ appropriately
- TestTokenExchangeServiceAccountNotFound: Verifies error handling when the target service account doesn't exist in the management cluster
- TestTokenValidationFails: Tests that invalid/expired tokens are rejected before exchange
- TestTokenExpirationPreserved: Confirms that token expiration times are preserved exactly during exchange
These tests follow Kubernetes upstream testing patterns and generate valid service account JWTs for realistic verification.
# Build the binary
make build
# Start the ext_authz server
./bin/tokensmith authz \
--addr 0.0.0.0 \
--port 9001 \
--workload-kubeconfig /path/to/workload/kubeconfig \
--token-expiration 3600 \
--log-level info \
--log-format jsonFlags:
--addr: Server address (default:0.0.0.0)--port: Server port (default:9001)--workload-kubeconfig: Path to workload cluster kubeconfig (if empty, uses in-cluster config)--token-expiration: Token expiration in seconds (default:3600= 1 hour)--log-level: Log level -debug,info,warn,error(default:info)--log-format: Log format -json,text(default:json)
Requirements:
- The server must have network access to both workload and management cluster API servers
- For management cluster: Uses in-cluster configuration (must run as a pod in the management cluster)
- For workload cluster: Provide kubeconfig via
--workload-kubeconfigor use in-cluster config - The management cluster service account needs permissions to create tokens:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: tokensmith rules: - apiGroups: [""] resources: ["serviceaccounts/token"] verbs: ["create"]
For testing and development, a simple greet service is also available:
# Start the greet server
./bin/tokensmith serve --port 8080
# Call the greet service
./bin/tokensmith greet --name Alice
# Output: Hello, Alice!Tokensmith uses identity-based mapping: it maps service accounts from the workload cluster to service accounts with the same namespace and name in the management cluster.
Example:
- Workload cluster SA:
app-prod/eso-sa - Management cluster SA:
app-prod/eso-sa(must exist)
Requirements:
- Service account must exist in management cluster with same namespace/name
- Management cluster RBAC controls what the service account can access
- No additional configuration needed - mapping is automatic based on identity
See AGENTS.md for development workflow and guidelines.
- Write a detailed plan to the plan/ directory
- Wait for human review and approval
- Implement the plan iteratively
- Create git commits at each todo step completion
- Update documentation
[To be determined]