Documentation
¶
Overview ¶
Package auth provides authentication and authorization interfaces and implementations for Kubernetes-based REST API services.
Authority: DD-AUTH-014 (Middleware-Based SAR Authentication)
This package implements a secure, testable authentication/authorization framework using dependency injection. The interfaces allow for: - Production: Real Kubernetes TokenReview + SubjectAccessReview (SAR) - Integration tests: Mock implementations (auth still enforced) - E2E tests: Real Kubernetes APIs in Kind clusters
Security: No runtime disable flags - auth is always enforced via interface implementations.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrTokenInvalid = errors.New("token not authenticated")
ErrTokenInvalid indicates the token was presented but rejected by the authenticator (e.g., expired, malformed, or not recognized). This is distinct from API call failures. Middleware uses this to return 401 instead of 500.
Functions ¶
func GetUserFromContext ¶
GetUserFromContext extracts the authenticated user identity from the request context. Returns empty string if user is not in context.
Types ¶
type AuthTransport ¶
type AuthTransport struct {
// contains filtered or unexported fields
}
AuthTransport is an http.RoundTripper that handles authentication for DataStorage API calls.
Behavior: - Reads token from /var/run/secrets/kubernetes.io/serviceaccount/token - Used by: ALL services in production, E2E, and integration (with mounted tokens) - Injects: Authorization: Bearer <token> - Caching: 5-minute cache to avoid filesystem reads on every request - Graceful degradation: If token file missing, request proceeds without auth
Thread Safety: - RoundTrip() is thread-safe (clones request, no shared state mutation) - Token caching uses sync.RWMutex for concurrent access
DD-AUTH-005: This transport enables all 7 Go services to authenticate with DataStorage without modifying the OpenAPI-generated client code.
ZERO TEST LOGIC: This production code contains no test-specific modes. For integration tests (mock user headers), use internal/mocks.NewMockUserTransport().
func NewServiceAccountTransport ¶
func NewServiceAccountTransport() *AuthTransport
NewServiceAccountTransport creates a transport that reads tokens from the ServiceAccount filesystem. Used by services in E2E/Production environments.
Token Path: /var/run/secrets/kubernetes.io/serviceaccount/token Caching: 5-minute cache to reduce filesystem reads Injects: Authorization: Bearer <token>
Usage:
transport := auth.NewServiceAccountTransport()
httpClient := &http.Client{Transport: transport}
dsClient := datastorage.NewClientWithResponses(url, datastorage.WithHTTPClient(httpClient))
DD-AUTH-005: This is the PRIMARY transport for production and E2E services. All 7 Go services use this via audit.NewOpenAPIClientAdapter().
func NewServiceAccountTransportWithBase ¶
func NewServiceAccountTransportWithBase(base http.RoundTripper) *AuthTransport
NewServiceAccountTransportWithBase creates a ServiceAccount transport with custom base transport. Useful for testing or custom transport configuration (e.g., custom timeouts, TLS).
func (*AuthTransport) RoundTrip ¶
RoundTrip implements http.RoundTripper. Injects authentication headers based on the transport mode before forwarding the request.
Thread Safety: Safe for concurrent use (clones request to avoid mutation).
DD-AUTH-005: This method is called automatically by the http.Client for every request. Services using the OpenAPI-generated client don't need to know about authentication.
type Authenticator ¶
type Authenticator interface {
// ValidateToken checks if the token is valid and returns the user identity.
//
// Parameters:
// - ctx: Request context (may include timeout)
// - token: Bearer token string (without "Bearer " prefix)
//
// Returns:
// - string: User identity (e.g., "system:serviceaccount:namespace:sa-name")
// - error: Token validation failure
//
// Errors:
// - Token is invalid or expired
// - Token cannot be authenticated
// - Kubernetes API call fails
ValidateToken(ctx context.Context, token string) (string, error)
}
Authenticator validates tokens and returns user identity.
Implementations:
- K8sAuthenticator: Uses Kubernetes TokenReview API (production/E2E)
- MockAuthenticator: Test double for integration tests
Example usage:
authenticator := auth.NewK8sAuthenticator(k8sClient)
user, err := authenticator.ValidateToken(ctx, "Bearer eyJhbGc...")
if err != nil {
// Return 401 Unauthorized
}
type Authorizer ¶
type Authorizer interface {
// CheckAccess verifies if the user has the required permissions.
//
// This method performs a Kubernetes SubjectAccessReview (SAR) check to determine
// if the specified user can perform the given verb on the specified resource.
//
// Parameters:
// - ctx: Request context (may include timeout)
// - user: User identity from token validation (e.g., "system:serviceaccount:ns:sa")
// - namespace: Kubernetes namespace for the resource
// - resource: Resource type (e.g., "services", "pods", "deployments")
// - resourceName: Specific resource name (e.g., "data-storage-service")
// - verb: RBAC verb (e.g., "create", "get", "list", "update", "delete")
//
// Returns:
// - bool: true if access is allowed, false if denied
// - error: SAR API call failure (not authorization denial)
//
// Errors:
// - Kubernetes API call fails
// - Invalid parameters
// - Network timeout
//
// Note: Authorization denial (user lacks permissions) returns (false, nil), not an error.
CheckAccess(ctx context.Context, user, namespace, resource, resourceName, verb string) (bool, error)
}
Authorizer checks if a user has permission to perform an action on a resource.
Implementations:
- K8sAuthorizer: Uses Kubernetes SubjectAccessReview (SAR) API (production/E2E)
- MockAuthorizer: Test double for integration tests
Example usage:
authorizer := auth.NewK8sAuthorizer(k8sClient)
allowed, err := authorizer.CheckAccess(
ctx,
"system:serviceaccount:kubernaut-system:datastorage",
"kubernaut-system", // namespace
"services", // resource
"data-storage-service", // resourceName
"create", // verb
)
if err != nil || !allowed {
// Return 403 Forbidden
}
type ContextKey ¶
type ContextKey string
ContextKey is the type for context keys used in the auth middleware.
const ( // UserContextKey is the context key for the authenticated user identity. UserContextKey ContextKey = "user" )
type K8sAuthenticator ¶
type K8sAuthenticator struct {
// contains filtered or unexported fields
}
K8sAuthenticator implements Authenticator using Kubernetes TokenReview API.
This implementation validates ServiceAccount tokens by making TokenReview API calls to the Kubernetes API server. It is suitable for production and E2E environments.
Authority: DD-AUTH-014
func NewK8sAuthenticator ¶
func NewK8sAuthenticator(client kubernetes.Interface) *K8sAuthenticator
NewK8sAuthenticator creates a new Kubernetes-based authenticator.
Example:
config, _ := rest.InClusterConfig() clientset, _ := kubernetes.NewForConfig(config) authenticator := auth.NewK8sAuthenticator(clientset)
func (*K8sAuthenticator) ValidateToken ¶
ValidateToken validates a ServiceAccount token using Kubernetes TokenReview API.
This method: 1. Creates a TokenReview request with the provided token 2. Sends the request to the Kubernetes API server 3. Returns the authenticated user identity if valid
Authority: https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/token-review-v1/
type K8sAuthorizer ¶
type K8sAuthorizer struct {
// contains filtered or unexported fields
}
K8sAuthorizer implements Authorizer using Kubernetes SubjectAccessReview (SAR) API.
This implementation checks RBAC permissions by making SAR API calls to the Kubernetes API server. It is suitable for production and E2E environments.
Authority: DD-AUTH-014
func NewK8sAuthorizer ¶
func NewK8sAuthorizer(client kubernetes.Interface) *K8sAuthorizer
NewK8sAuthorizer creates a new Kubernetes-based authorizer.
Example:
config, _ := rest.InClusterConfig() clientset, _ := kubernetes.NewForConfig(config) authorizer := auth.NewK8sAuthorizer(clientset)
func (*K8sAuthorizer) CheckAccess ¶
func (a *K8sAuthorizer) CheckAccess(ctx context.Context, user, namespace, resource, resourceName, verb string) (bool, error)
CheckAccess checks if a user has permission using Kubernetes SubjectAccessReview API.
This method: 1. Creates a SubjectAccessReview request with the provided parameters 2. Sends the request to the Kubernetes API server 3. Returns true if the user is allowed, false if denied
type Middleware ¶
type Middleware struct {
// contains filtered or unexported fields
}
Middleware provides authentication and authorization for HTTP requests.
Authority: DD-AUTH-014 (Middleware-Based SAR Authentication)
This middleware implements a secure, testable auth framework using dependency injection: 1. Extracts Bearer token from Authorization header 2. Validates token using Authenticator interface (TokenReview) 3. Checks authorization using Authorizer interface (SAR) 4. Injects user identity into request context (for audit logging) 5. Injects X-Auth-Request-User header (for SOC2 user attribution)
Security: No runtime disable flags — auth is always enforced via interface implementations.
Used by: DataStorage service (BR-DS-*), Gateway service (BR-GATEWAY-036/037)
func NewMiddleware ¶
func NewMiddleware( authenticator Authenticator, authorizer Authorizer, config MiddlewareConfig, logger logr.Logger, ) *Middleware
NewMiddleware creates a new authentication middleware with dependency injection.
Example (Production):
k8sClient, _ := kubernetes.NewForConfig(config)
authenticator := auth.NewK8sAuthenticator(k8sClient)
authorizer := auth.NewK8sAuthorizer(k8sClient)
mw := auth.NewMiddleware(
authenticator,
authorizer,
auth.MiddlewareConfig{
Namespace: "kubernaut-system",
Resource: "services",
ResourceName: "gateway-service",
Verb: "create",
},
logger,
)
func (*Middleware) Handler ¶
func (m *Middleware) Handler(next http.Handler) http.Handler
Handler returns a chi-compatible middleware handler.
HTTP Status Codes (Authority: DD-AUTH-013): - 401 Unauthorized: Missing/invalid/expired token - 403 Forbidden: Valid token but insufficient RBAC permissions - 500 Internal Server Error: TokenReview/SAR API call failures
type MiddlewareConfig ¶
type MiddlewareConfig struct {
// Namespace is the Kubernetes namespace for the SAR check
Namespace string
// Resource is the Kubernetes resource type (e.g., "services", "pods")
Resource string
// ResourceName is the specific resource name (e.g., "data-storage-service", "gateway-service")
ResourceName string
// Verb is the RBAC verb to check (e.g., "create", "get", "list", "update", "delete")
// Authority: DD-AUTH-011 (Granular RBAC with SAR verb mapping)
Verb string
}
MiddlewareConfig contains the SAR configuration for authorization checks.
type MockAuthenticator ¶
type MockAuthenticator struct {
// ValidUsers maps tokens to user identities.
// Key: token string
// Value: user identity (e.g., "system:serviceaccount:namespace:sa-name")
ValidUsers map[string]string
// ErrorToReturn allows tests to simulate TokenReview API failures.
// If set, ValidateToken will return this error instead of checking ValidUsers.
ErrorToReturn error
// CallCount tracks how many times ValidateToken was called.
// Useful for verifying caching behavior.
CallCount int
}
MockAuthenticator is a test double implementation of Authenticator.
This mock is intended for integration tests where we want to validate authentication flow without making real Kubernetes API calls. The mock allows tests to control which tokens are valid and which users they map to.
Authority: DD-AUTH-014
Security Note: This mock is ONLY for testing purposes. Production code (cmd/) always uses K8sAuthenticator with real Kubernetes APIs.
Example usage in integration tests:
authenticator := &auth.MockAuthenticator{
ValidUsers: map[string]string{
"test-token-authorized": "system:serviceaccount:test:authorized-sa",
"test-token-readonly": "system:serviceaccount:test:readonly-sa",
},
}
// Test with valid token
user, err := authenticator.ValidateToken(ctx, "test-token-authorized")
// Returns: "system:serviceaccount:test:authorized-sa", nil
// Test with invalid token
user, err := authenticator.ValidateToken(ctx, "invalid-token")
// Returns: "", error
func (*MockAuthenticator) ValidateToken ¶
ValidateToken implements the Authenticator interface for testing.
type MockAuthorizer ¶
type MockAuthorizer struct {
// AllowedUsers maps user identities to authorization decisions.
// Key: user identity (e.g., "system:serviceaccount:namespace:sa-name")
// Value: true if allowed, false if denied
//
// If a user is not in the map, access is denied by default (secure default).
AllowedUsers map[string]bool
// PerResourceDecisions allows fine-grained control for tests that need
// different authorization results based on the resource being accessed.
// Key: "namespace/resource/resourceName/verb" (e.g., "kubernaut-system/services/data-storage-service/create")
// Value: map of user -> allowed
//
// If set, PerResourceDecisions takes precedence over AllowedUsers.
PerResourceDecisions map[string]map[string]bool
// ErrorToReturn allows tests to simulate SAR API failures.
// If set, CheckAccess will return this error instead of checking AllowedUsers.
ErrorToReturn error
// CallCount tracks how many times CheckAccess was called.
// Useful for verifying caching behavior.
CallCount int
}
MockAuthorizer is a test double implementation of Authorizer.
This mock is intended for integration tests where we want to validate authorization flow without making real Kubernetes SAR API calls. The mock allows tests to control which users are allowed access.
Authority: DD-AUTH-014
Security Note: This mock is ONLY for testing purposes. Production code (cmd/) always uses K8sAuthorizer with real Kubernetes APIs.
Example usage in integration tests:
authorizer := &auth.MockAuthorizer{
AllowedUsers: map[string]bool{
"system:serviceaccount:test:authorized-sa": true,
"system:serviceaccount:test:readonly-sa": false,
},
}
// Test with authorized user
allowed, err := authorizer.CheckAccess(
ctx,
"system:serviceaccount:test:authorized-sa",
"kubernaut-system",
"services",
"data-storage-service",
"create",
)
// Returns: true, nil
// Test with unauthorized user
allowed, err := authorizer.CheckAccess(
ctx,
"system:serviceaccount:test:readonly-sa",
"kubernaut-system",
"services",
"data-storage-service",
"create",
)
// Returns: false, nil
func (*MockAuthorizer) CheckAccess ¶
func (a *MockAuthorizer) CheckAccess(ctx context.Context, user, namespace, resource, resourceName, verb string) (bool, error)
CheckAccess implements the Authorizer interface for testing.