Skip to content

Conversation

NickCao
Copy link
Collaborator

@NickCao NickCao commented Jan 31, 2025

Summary by CodeRabbit

  • New API Resources
    • Introduced an exporter access policy with role-based rules for administrators, developers, and CI systems.
  • Authentication Enhancements
    • Integrated advanced OIDC authentication with self-signed certificates and dynamic token signing.
    • Added a new configuration sample for Dex server setup.
  • Schema Enhancements
    • Extended client and exporter resource definitions with a new username field.
    • Enhanced lease support with priority and spot access attributes.
  • Helm & RBAC Updates
    • Updated resource configurations and permissions to support the new functionality.
  • Configuration Updates
    • Added a new dex.yaml configuration file for Dex server setup.
    • Introduced a new ConfigMap for the Jumpstarter controller to manage authentication configurations.
  • Testing Enhancements
    • Updated tests to integrate OIDC signing capabilities for client and exporter reconciliation processes.

Copy link
Contributor

coderabbitai bot commented Jan 31, 2025

Walkthrough

This pull request introduces a new API resource—ExporterAccessPolicy—while enhancing multiple API types. Several new structs and fields are added for authentication, client, exporter, and lease configurations. Deep copy methods are updated, and numerous CRDs and sample YAMLs are modified. Changes are also applied to Helm charts, dependency updates in go.mod, and internal packages for authentication, authorization, OIDC, and controller logic. Removed files include the mock server and token-related code, marking a shift towards an integrated OIDC-based security model.

Changes

File(s) Change Summary
api/v1alpha1/exporteraccesspolicy_types.go
api/v1alpha1/authenticationconfiguration_types.go
api/v1alpha1/client_types.go
api/v1alpha1/exporter_types.go
api/v1alpha1/lease_types.go
Added new API types and fields including ExporterAccessPolicy, AuthenticationConfiguration, and new fields (e.g., Username, Priority, SpotAccess) to enhance resource definitions.
api/v1alpha1/zz_generated.deepcopy.go Introduced deep copy methods for new and updated API structures to ensure proper object copying.
cmd/main.go
cmd/mock/main.go
Updated main application to include OIDC certificate and signer initialization, configuration loading, and removed the mock server implementation.
config/samples/*.yaml Added and modified sample YAML configurations for Dex, kustomization, Client, and ExporterAccessPolicy resource definitions.
deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/* Updated Helm charts by adding new CRDs, a ConfigMap, and properties (e.g., username, priority, spotAccess) in clients, exporters, leases, and RBAC roles.
go.mod Modified Go version and updated dependencies: added new ones (e.g., filippo.io/keygen, github.com/go-jose/go-jose/v4) while upgrading/downgrading existing ones.
internal/authentication/*
internal/authorization/*
Refactored authentication and authorization packages: changed package name, added BearerTokenAuthenticator, ContextAuthenticator interface, BasicAuthorizer, and support for metadata attribute extraction.
internal/config/config.go Added LoadConfiguration function to load and parse authentication configuration from a ConfigMap using OIDC components.
internal/controller/client_controller.go
internal/controller/exporter_controller.go
internal/controller/lease_controller.go
Modified controller logic to include a new OIDC Signer for token generation and policy-based exporter selection.
internal/controller/token.go Removed token handling file as signing and verification is now managed via OIDC modules.
internal/oidc/* Introduced a new OIDC package with Signer functionality, configuration loading, and token verification features.
internal/service/controller_service.go
internal/service/oidc_service.go
internal/service/router_service.go
Extended ControllerService and RouterService to support new OIDC authentication/authorization flows; added a new OIDCService exposing gRPC endpoints.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ControllerService
    participant OIDCService
    participant OIDCSigner

    Note over Client,ControllerService: OIDC Authentication Flow
    Client->>ControllerService: Send request with token
    ControllerService->>OIDCService: Authenticate token using Authn, Authz, Attr
    OIDCService->>OIDCSigner: Validate token / generate new JWT
    OIDCSigner-->>OIDCService: Return token status & details
    OIDCService-->>ControllerService: Relay authentication result
    ControllerService-->>Client: Respond with authorized access
Loading

Poem

I'm a bunny in the code garden, hopping with glee,
New policies and tokens dance merrily with me.
OIDC secrets whisper in the moonlit night,
As deep copies and CRDs take flight.
With every commit, my little paws tap in delight,
Celebrating these changes with a joyful byte!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 012ee1c and 48775b4.

📒 Files selected for processing (1)
  • cmd/main.go (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: e2e-tests
  • GitHub Check: lint-go
  • GitHub Check: tests
  • GitHub Check: deploy-kind
🔇 Additional comments (5)
cmd/main.go (5)

19-50: LGTM! Import changes and scheme installation look good.

The new imports and apiserver scheme installation are correctly implemented to support the OIDC functionality.

Also applies to: 57-64


138-153: Validate CONTROLLER_KEY environment variable.

While error handling is implemented for certificate and signer creation, the CONTROLLER_KEY environment variable should be validated before use.

Add validation before using the environment variable:

+	controllerKey := os.Getenv("CONTROLLER_KEY")
+	if controllerKey == "" {
+		setupLog.Error(nil, "CONTROLLER_KEY environment variable is not set")
+		os.Exit(1)
+	}
+
 	oidcSigner, err := oidc.NewSignerFromSeed(
-		[]byte(os.Getenv("CONTROLLER_KEY")),
+		[]byte(controllerKey),
 		"https://localhost:8085",
 		"jumpstarter",
 		"internal:",
 	)

155-172: Validate NAMESPACE environment variable.

Similar to CONTROLLER_KEY, the NAMESPACE environment variable should be validated before use.

Add validation before using the environment variable:

+	namespace := os.Getenv("NAMESPACE")
+	if namespace == "" {
+		setupLog.Error(nil, "NAMESPACE environment variable is not set")
+		os.Exit(1)
+	}
+
 	authenticator, err := config.LoadConfiguration(
 		context.Background(),
 		mgr.GetAPIReader(),
 		mgr.GetScheme(),
 		client.ObjectKey{
-			Namespace: os.Getenv("NAMESPACE"),
+			Namespace: namespace,
 			Name:      "jumpstarter-controller",
 		},
 		oidcSigner,

174-189: LGTM! Controller updates are properly implemented.

The OIDC signer is correctly integrated into the ExporterReconciler and ClientReconciler.


205-234: LGTM! Service updates implement proper security components.

The authentication and authorization components are correctly integrated into the ControllerService, and the new OIDCService is properly configured.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@NickCao NickCao force-pushed the oidc-design branch 6 times, most recently from 6f037fa to 13591b6 Compare February 6, 2025 16:11
@NickCao NickCao marked this pull request as ready for review February 6, 2025 16:16
@NickCao NickCao changed the title OIDC alternative design Implement OIDC based authentication and authorization Feb 6, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🔭 Outside diff range comments (3)
internal/service/controller_service.go (1)

525-527: Correct variable assignment for EndTime

In the GetLease method, when lease.Status.EndTime is not nil, the code incorrectly assigns beginTime instead of endTime. This results in endTime remaining nil even when it should have a value. Please assign the value to endTime instead.

Apply this diff to fix the assignment:

 var endTime *timestamppb.Timestamp
 if lease.Status.EndTime != nil {
-	beginTime = timestamppb.New(lease.Status.EndTime.Time)
+	endTime = timestamppb.New(lease.Status.EndTime.Time)
 }
 var exporterUuid *string
api/v1alpha1/exporter_types.go (1)

46-47: Fix incorrect type reference.

The condition type constants are using LeaseConditionType instead of ExporterConditionType.

-	ExporterConditionTypeRegistered LeaseConditionType = "Registered"
-	ExporterConditionTypeOnline     LeaseConditionType = "Online"
+	ExporterConditionTypeRegistered ExporterConditionType = "Registered"
+	ExporterConditionTypeOnline     ExporterConditionType = "Online"
internal/service/router_service.go (1)

52-78: Enhance JWT validation security.

While the JWT validation is good, consider these security improvements:

  1. Use environment variables for all JWT configuration
  2. Add token expiry time validation
  3. Consider adding token replay protection
 func (s *RouterService) authenticate(ctx context.Context) (string, error) {
     token, err := authentication.BearerTokenFromContext(ctx)
     if err != nil {
         return "", err
     }

+    // Load configuration from environment
+    key := os.Getenv("ROUTER_KEY")
+    if key == "" {
+        return "", status.Errorf(codes.Internal, "router key not configured")
+    }
+
+    maxTokenAge := 5 * time.Minute // Consider making this configurable
+
     parsed, err := jwt.ParseWithClaims(
         token,
         &jwt.RegisteredClaims{},
-        func(t *jwt.Token) (interface{}, error) { return []byte(os.Getenv("ROUTER_KEY")), nil },
+        func(t *jwt.Token) (interface{}, error) { return []byte(key), nil },
         jwt.WithIssuer("https://jumpstarter.dev/stream"),
         jwt.WithAudience("https://jumpstarter.dev/router"),
         jwt.WithIssuedAt(),
         jwt.WithExpirationRequired(),
+        jwt.WithLeeway(30 * time.Second), // Allow for clock skew
         jwt.WithValidMethods([]string{
             jwt.SigningMethodHS256.Name,
             jwt.SigningMethodHS384.Name,
             jwt.SigningMethodHS512.Name,
         }),
     )

     if err != nil || !parsed.Valid {
         return "", status.Errorf(codes.InvalidArgument, "invalid jwt token")
     }

+    // Validate token age
+    if claims, ok := parsed.Claims.(*jwt.RegisteredClaims); ok {
+        if time.Since(claims.IssuedAt.Time) > maxTokenAge {
+            return "", status.Errorf(codes.InvalidArgument, "token too old")
+        }
+    }

     return parsed.Claims.GetSubject()
 }
🧹 Nitpick comments (18)
internal/authorization/metadata.go (2)

15-19: Document that metadata keys should be lower-case

To prevent potential issues with metadata key retrieval due to case sensitivity, it's important to note that gRPC metadata keys are expected to be lower-case. Adding comments to the MetadataAttributesGetterConfig fields will enhance code clarity and maintainability.

Apply this diff to add comments:

 type MetadataAttributesGetterConfig struct {
+    // NamespaceKey is the metadata key for the namespace (expected in lower-case).
     NamespaceKey string
+    // ResourceKey is the metadata key for the resource (expected in lower-case).
     ResourceKey  string
+    // NameKey is the metadata key for the name (expected in lower-case).
     NameKey      string
 }

63-72: Normalize metadata keys to lower-case in mdGet function

Since gRPC metadata keys are normalized to lower-case, ensuring that the keys used in mdGet are also lower-case will prevent potential mismatches due to case differences.

Apply this diff to normalize keys in mdGet:

+import "strings"

 func mdGet(md metadata.MD, k string) (string, error) {
+    k = strings.ToLower(k)
     v := md.Get(k)
     if len(v) < 1 {
         return "", status.Errorf(codes.InvalidArgument, "missing metadata: %s", k)
     }
     if len(v) > 1 {
         return "", status.Errorf(codes.InvalidArgument, "multiple metadata: %s", k)
     }
     return v[0], nil
 }

Ensure the strings package is imported.

internal/authentication/bearer.go (1)

58-59: Trim whitespace from the extracted token

When extracting the token from the authorization header, leading or trailing whitespaces may cause token validation to fail. It's advisable to trim any whitespace from the token to ensure accurate authentication.

Apply this diff to trim the token:

 token := authorization[7:]
+token = strings.TrimSpace(token)

Ensure the strings package is imported.

internal/authorization/basic.go (1)

24-53: Handle unknown resource types explicitly

In the Authorize method, the default case returns a DecisionDeny with the reason "invalid object kind". It might be better to log or handle unknown resource types to aid in debugging and provide clearer error messaging.

Consider updating the default case:

 default:
+    // Log the unknown resource type
+    // log.Printf("Unknown resource type: %s", attributes.GetResource())
     return authorizer.DecisionDeny, "unknown resource type", nil
internal/oidc/token.go (1)

28-30: Provide more specific authentication failure messages.

Returning a generic error message "failed to authenticate token" may not provide sufficient context. Consider returning more detailed error messages or using structured error handling to assist in debugging authentication issues.

internal/oidc/config.go (1)

25-34: Avoid mutating the input configuration object.

The function modifies the authenticationConfiguration by appending to JWT, which alters the original input. To prevent unexpected side effects, consider creating a copy of the configuration before making modifications.

Apply this diff to create a copy:

func LoadAuthenticationConfiguration(
	ctx context.Context,
	scheme *runtime.Scheme,
	configuration []byte,
	signer *Signer,
	certificateAuthority string,
) (authenticator.Token, error) {
+	var authenticationConfigurationCopy jumpstarterdevv1alpha1.AuthenticationConfiguration
+	if err := deepcopy.Copy(&authenticationConfigurationCopy, &authenticationConfiguration); err != nil {
+		return nil, err
+	}
+	authenticationConfiguration = authenticationConfigurationCopy

	authenticationConfiguration.JWT = append(authenticationConfiguration.JWT, apiserverv1beta1.JWTAuthenticator{
		// existing code...
	})
	// rest of the function...
}
internal/controller/lease_controller.go (1)

353-359: Select exporter based on policy priority

Currently, the first approved exporter is selected without considering the Policy.Priority. To ensure that the exporter with the highest priority is chosen, please sort the approvedExporters slice based on the Policy.Priority before selecting the exporter.

Apply this diff to sort the approved exporters by priority:

+	// Sort approved exporters by priority in descending order
+	sort.SliceStable(approvedExporters, func(i, j int) bool {
+		return approvedExporters[i].Policy.Priority > approvedExporters[j].Policy.Priority
+	})

	selected := approvedExporters[0]
	lease.Status.Priority = selected.Policy.Priority
	lease.Status.SpotAccess = selected.Policy.SpotAccess
	lease.Status.ExporterRef = &corev1.LocalObjectReference{
		Name: selected.Exporter.Name,
	}

Remember to import the sort package at the top of the file:

 import (
 	"context"
 	"fmt"
 	"slices"
+	"sort"
 	"time"
internal/config/config.go (2)

14-21: Consider adding parameter validation.

The function takes several critical parameters but lacks validation. Consider adding checks for nil values of client, scheme, signer, and empty certificateAuthority string.

 func LoadConfiguration(
 	ctx context.Context,
 	client client.Reader,
 	scheme *runtime.Scheme,
 	key client.ObjectKey,
 	signer *oidc.Signer,
 	certificateAuthority string,
 ) (authenticator.Token, error) {
+	if client == nil {
+		return nil, fmt.Errorf("client cannot be nil")
+	}
+	if scheme == nil {
+		return nil, fmt.Errorf("scheme cannot be nil")
+	}
+	if signer == nil {
+		return nil, fmt.Errorf("signer cannot be nil")
+	}
+	if certificateAuthority == "" {
+		return nil, fmt.Errorf("certificateAuthority cannot be empty")
+	}

32-41: Consider adding context timeout.

The OIDC configuration loading might take time. Consider adding a timeout to prevent long-running operations.

+	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
+	defer cancel()
 	authenticator, err := oidc.LoadAuthenticationConfiguration(
 		ctx,
 		scheme,
 		[]byte(rawAuthenticationConfiguration),
 		signer,
 		certificateAuthority,
 	)
api/v1alpha1/client_types.go (1)

29-29: Consider adding field validation.

The optional Username field might benefit from validation rules using kubebuilder markers.

+// +kubebuilder:validation:Pattern=^[a-zA-Z0-9_-]+$
+// +kubebuilder:validation:MinLength=1
+// +kubebuilder:validation:MaxLength=32
 Username *string `json:"username,omitempty"`
api/v1alpha1/exporter_types.go (1)

29-29: Consider adding field validation and documentation.

The optional Username field should have the same validation rules as in ClientSpec for consistency.

+// Username is the identity of the exporter used for OIDC authentication.
+// +kubebuilder:validation:Pattern=^[a-zA-Z0-9_-]+$
+// +kubebuilder:validation:MinLength=1
+// +kubebuilder:validation:MaxLength=32
 Username *string `json:"username,omitempty"`
api/v1alpha1/exporteraccesspolicy_types.go (2)

26-28: Add validation and documentation for ClientSelector.

Consider adding:

  • Validation markers to ensure valid label selectors
  • Documentation about the expected label format and usage
 type From struct {
+    // ClientSelector is a label selector that determines which clients this policy applies to.
+    // +kubebuilder:validation:Required
     ClientSelector metav1.LabelSelector `json:"clientSelector,omitempty"`
 }

30-35: Add validation constraints and documentation for Policy fields.

The Policy struct needs validation markers and clear documentation for its fields:

  • Priority should have min/max values
  • MaximumDuration should have reasonable bounds
 type Policy struct {
+    // Priority determines the order in which policies are evaluated.
+    // Higher numbers indicate higher priority.
+    // +kubebuilder:validation:Minimum=-100
+    // +kubebuilder:validation:Maximum=100
     Priority        int              `json:"priority,omitempty"`
+    // From defines the clients this policy applies to
     From            []From           `json:"from,omitempty"`
+    // MaximumDuration specifies the maximum time a lease can be held
+    // +kubebuilder:validation:Format=duration
     MaximumDuration *metav1.Duration `json:"maximumDuration,omitempty"`
+    // SpotAccess indicates if this is a preemptible lease
     SpotAccess      bool             `json:"spotAccess,omitempty"`
 }
api/v1alpha1/lease_types.go (1)

44-45: Add documentation for new status fields.

The new Priority and SpotAccess fields need documentation to explain their relationship with ExporterAccessPolicy.

+    // Priority reflects the priority level of the policy that granted this lease
     Priority    int                          `json:"priority,omitempty"`
+    // SpotAccess indicates whether this lease was granted as a preemptible lease
     SpotAccess  bool                         `json:"spotAccess,omitempty"`
internal/controller/client_controller.go (1)

116-137: Improve error handling and logging in secretForClient.

The error handling could be more descriptive and include logging for better debugging.

-func (r *ClientReconciler) secretForClient(client *jumpstarterdevv1alpha1.Client) (*corev1.Secret, error) {
+func (r *ClientReconciler) secretForClient(client *jumpstarterdevv1alpha1.Client) (*corev1.Secret, error) {
+    if r.Signer == nil {
+        return nil, fmt.Errorf("OIDC signer not initialized")
+    }
+
     token, err := r.Signer.Token(authorization.ClientAuthorizedUsername(client, r.Signer.Prefix()))
     if err != nil {
-        return nil, err
+        return nil, fmt.Errorf("failed to generate token: %w", err)
     }

     secret := &corev1.Secret{
config/samples/dex.yaml (1)

1-1: Consider using a configurable issuer URL.

The hardcoded IP address 10.239.206.8 might not be portable across different environments.

Consider using environment variables or configuration maps:

-issuer: http://10.239.206.8:5556/dex
+issuer: ${DEX_ISSUER_URL}
deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml (1)

41-43: New 'username' Field in Exporter Spec

The addition of the username property as a string in the spec section enriches the Exporter CRD schema. Consider adding a brief description for this field (e.g., its purpose or any validation constraints) to improve clarity and future maintainability.

deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml (1)

92-156: Policies Field Structure

The policies array is comprehensively defined, including nested fields such as from with its own clientSelector label setup. This design supports complex policy definitions. It may be beneficial to further clarify the expected format of fields like maximumDuration (for example, specifying ISO 8601 duration) and to outline any bounds or valid ranges for priority if applicable.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18e3315 and 13591b6.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (36)
  • PROJECT (1 hunks)
  • api/v1alpha1/authenticationconfiguration_types.go (1 hunks)
  • api/v1alpha1/client_types.go (1 hunks)
  • api/v1alpha1/exporter_types.go (1 hunks)
  • api/v1alpha1/exporteraccesspolicy_types.go (1 hunks)
  • api/v1alpha1/lease_types.go (1 hunks)
  • api/v1alpha1/zz_generated.deepcopy.go (7 hunks)
  • cmd/main.go (6 hunks)
  • cmd/mock/main.go (0 hunks)
  • config/samples/dex.yaml (1 hunks)
  • config/samples/kustomization.yaml (1 hunks)
  • config/samples/v1alpha1_client.yaml (1 hunks)
  • config/samples/v1alpha1_exporteraccesspolicy.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml (1 hunks)
  • go.mod (3 hunks)
  • internal/authentication/bearer.go (2 hunks)
  • internal/authentication/types.go (1 hunks)
  • internal/authorization/basic.go (1 hunks)
  • internal/authorization/metadata.go (1 hunks)
  • internal/authorization/types.go (1 hunks)
  • internal/config/config.go (1 hunks)
  • internal/controller/client_controller.go (2 hunks)
  • internal/controller/exporter_controller.go (2 hunks)
  • internal/controller/lease_controller.go (3 hunks)
  • internal/controller/token.go (0 hunks)
  • internal/oidc/config.go (1 hunks)
  • internal/oidc/op.go (1 hunks)
  • internal/oidc/token.go (1 hunks)
  • internal/service/controller_service.go (4 hunks)
  • internal/service/oidc_service.go (1 hunks)
  • internal/service/router_service.go (2 hunks)
💤 Files with no reviewable changes (2)
  • cmd/mock/main.go
  • internal/controller/token.go
✅ Files skipped from review due to trivial changes (1)
  • config/samples/v1alpha1_client.yaml
🧰 Additional context used
🪛 YAMLlint (1.35.1)
deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml

[error] 10-10: syntax error: could not find expected ':'

(syntax)

🔇 Additional comments (28)
internal/oidc/token.go (2)

47-49: Verify the correctness of resource name in authorization checks.

The check attrs.GetResource() != "Client" assumes the resource name is "Client". Kubernetes resource names are typically lowercase and plural, such as "clients". Ensure that the resource name matches the actual Kubernetes resource naming conventions.


83-85: Verify the correctness of resource name in authorization checks.

Similarly, the check attrs.GetResource() != "Exporter" assumes the resource name is "Exporter". Confirm that this matches the Kubernetes resource naming conventions, which are usually lowercase and plural, like "exporters".

internal/oidc/config.go (1)

62-89: Ensure proper handling when no JWT authenticators are configured.

If authenticationConfiguration.JWT is empty, the function may return an authenticator that denies all tokens. Verify that at least one JWT authenticator is provided or handle the scenario where none are configured to prevent unintended authentication failures.

internal/authentication/types.go (1)

9-11: LGTM! Well-designed interface following Kubernetes patterns.

The ContextAuthenticator interface is well-defined, focused, and follows Kubernetes authentication patterns.

internal/authorization/types.go (1)

10-12: LGTM! Well-designed interface following Kubernetes patterns.

The ContextAttributesGetter interface is well-defined, focused, and follows Kubernetes authorization patterns.

api/v1alpha1/authenticationconfiguration_types.go (1)

8-15: LGTM! Well-designed type following Kubernetes CRD patterns.

The AuthenticationConfiguration type is well-defined, properly documented, and follows Kubernetes CRD patterns.

internal/config/config.go (2)

22-25: LGTM!

Good error handling for ConfigMap retrieval.


27-30: LGTM!

Clear error message for missing authentication section.

PROJECT (1)

31-37: LGTM!

The new ExporterAccessPolicy resource is properly configured with appropriate CRD version and namespace settings.

internal/controller/exporter_controller.go (4)

32-33: LGTM! New imports for authorization and OIDC.

The imports align with the OIDC alternative design implementation.


40-40: LGTM! Added Signer field for OIDC token generation.

The Signer field is correctly typed as *oidc.Signer for handling token generation.


46-46: LGTM! Added RBAC permission for ExporterAccessPolicy.

The new RBAC permission correctly grants read-only access (get, list, watch) to the exporteraccesspolicies resource.


157-158: LGTM! Updated token generation using OIDC Signer.

The token generation now uses the OIDC signer with proper authorization username formatting.

Let's verify the token generation by checking the authorization package implementation:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Verify the ExporterAuthorizedUsername function implementation

ast-grep --pattern 'func ExporterAuthorizedUsername($_, $_) string {
  $$$
}'

Length of output: 545


Token generation has been verified.
The ExporterAuthorizedUsername function in internal/authorization/basic.go correctly formats the username by appending the prefix and exporter details when no custom username is specified, and by returning the provided username otherwise. This aligns with the updated token generation approach using the OIDC signer.

api/v1alpha1/zz_generated.deepcopy.go (1)

30-555: LGTM! Auto-generated deep copy methods for new types.

The deep copy methods are correctly generated for:

  • AuthenticationConfiguration with JWT field
  • ExporterAccessPolicy with its spec and status
  • From with ClientSelector
  • Policy with From and MaximumDuration fields
config/samples/kustomization.yaml (1)

6-6: LGTM! Added ExporterAccessPolicy sample resource.

The new resource is correctly added to the kustomization configuration.

config/samples/dex.yaml (2)

12-14: Review static client credentials for production use.

The static client configuration uses a simple "secret" as the client secret, which is not secure for production environments.

Please ensure this is only used for development/testing purposes. For production, consider:

  1. Using a more secure secret generation method
  2. Storing secrets in Kubernetes secrets
  3. Implementing client credential rotation

19-26: Secure the static password configuration.

The configuration includes:

  1. Password values visible in comments
  2. Static user IDs
  3. Fixed bcrypt hash for both users

For production environments:

  1. Remove password comments
  2. Use different strong passwords for each user
  3. Consider implementing an external user management system
deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml (1)

49-56: New RBAC rule for ExporterAccessPolicy.
A new rule for the exporteraccesspolicies resource has been added with read-only verbs (get, list, watch). Verify that this read-only access is aligned with your intended management strategy for ExporterAccessPolicy resources. If these objects are expected to be created or modified by controllers or users, you might need to include additional verbs (such as create, update, or delete).

config/samples/v1alpha1_exporteraccesspolicy.yaml (1)

1-31: New sample YAML for ExporterAccessPolicy.
This new sample file is well-structured and clearly defines an ExporterAccessPolicy with an exporter selector and a list of policies (with varying priorities and constraints). Please ensure that the field names (e.g., exporterSelector and policies) and their values match the definitions in your CRD and API types for consistency.

deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml (2)

9-11: Review Helm Templating in Metadata Labels.
The conditional block that injects deployment.timestamp based on .Values.global.timestamp is correctly set up. However, YAML linting reported an error (syntax error: could not find expected ':') on these lines. This is likely due to the Helm templating syntax interfering with YAML parsing. Consider wrapping the templated expression in quotes or using the toYaml function to ensure that the rendered YAML is syntactically correct.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 10-10: syntax error: could not find expected ':'

(syntax)


12-40: Authentication Configuration in ConfigMap.
The multi-line authentication field correctly embeds an AuthenticationConfiguration using the JWT configuration. Verify that the issuer URL, audience, and certificate authority are current and secured for your OIDC alternative design. The mapping for the username (using the "sub" claim) is appropriately defined.

deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml (1)

41-44: New 'username' Field in Client CRD.
The addition of the username field under the spec section is clear and helps extend the Client resource schema. Please make sure that the corresponding API type and any validation logic (if applicable) reflect this change consistently.

go.mod (3)

1-4: Go Version Update.
The Go version has been changed to go 1.22.0 (downgraded from 1.22.3). Please confirm that this downgrade is intentional and that it remains compatible with your codebase and dependency requirements.


5-26: Updated and New Dependencies.
New dependencies—including filippo.io/keygen, github.com/go-jose/go-jose/v4, and github.com/zitadel/oidc/v3—have been added to support the OIDC alternative design. Several dependencies (such as the testing libraries ginkgo and gomega) have also been updated. Ensure that these changes have been tested thoroughly to avoid compatibility issues.


28-130: Indirect Dependencies Verification.
The indirect dependencies have been updated alongside your core module requirements. It is advisable to double-check that there are no version conflicts between these newly added or updated modules.

deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml (2)

1-7: New ExporterAccessPolicy CRD Definition

This entirely new CRD is well-structured and adheres to Kubernetes API conventions. The metadata, versioning, and annotations are correctly defined.


43-91: ExporterSelector Field Definition

The exporterSelector field is defined using both matchExpressions and matchLabels, offering flexible resource selection capabilities. The structure follows best practices for Kubernetes label selectors. If there are any specific constraints or usage expectations for the username or other labels on Exporters, consider elaborating that in an additional description.

deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml (1)

210-213: Addition of 'priority' and 'spotAccess' in Lease CRD Status

The new properties priority (integer) and spotAccess (boolean) in the status schema effectively extend the Lease CRD to support enhanced status reporting. Please verify that these fields are mirrored in the corresponding LeaseStatus struct (in api/v1alpha1/lease_types.go) and that any business logic processing these fields is updated accordingly.

@NickCao NickCao force-pushed the oidc-design branch 3 times, most recently from d69bcec to 2c0f06a Compare February 6, 2025 16:30
@NickCao
Copy link
Collaborator Author

NickCao commented Feb 6, 2025

Summary of changes for reviewer:

AuthenticationConfiguration

Borrowed from https://kubernetes.io/docs/reference/access-authn-authz/authentication/#using-authentication-configuration

Allows you to specify multiple upstream OIDC providers and how to map upstream usernames to jumpstarter usernames.

e.g. Alice logged in from github might be mapped to github:Alice, while Bob logged in from google will be mapped to google:Bob, the prefix is there to ensure everyone gets a unique jumpstarter username. Additionally, an internal OIDC provider is automatically injected to the configuration, which maps to usernames with internal: prefix.

ClientSpec/ExporterSpec

A new optional field Username is added to ClientSpec and ExporterSpec, representing the jumpstarter user allowed to login as the client/exporter. If left empty, the internal OIDC provider and a automatically generated static token would be used instead.

ExporterAccessPolicy

A new CRD ExporterAccessPolicy is added to control which Client can lease which Exporter for how long. If no ExporterAccessPolicy is present, all access is allowed.

LeaseStatus

Two new fields Priority and SpotAccess are added to LeaseStatus, they would be used in the future for implementing lease preemption.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
internal/oidc/op.go (2)

37-46: ⚠️ Potential issue

Use cryptographically secure key generation.
This code uses math/rand seeded by sha256, which is deterministic. For production-grade security, switch to crypto/rand to ensure truly unpredictable keys.


93-106: ⚠️ Potential issue

Revise handling for external subjects and reduce token lifetime.

  1. Returning a placeholder token for external OIDC providers is insufficient and may lead to security gaps; consider returning an explicit error if not supported.
  2. The 365-day token lifetime is too long. Short token lifetimes (e.g., 1 hour) plus key rotation mitigates token misuse.
🧹 Nitpick comments (15)
deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml (1)

16-39: OIDC JWT Configuration (Commented Block).

The commented-out JWT configuration block provides a useful template for enabling OIDC. When you’re ready to activate OIDC, remember to update the issuer URL, audiences, and certificate authority accordingly, and then uncomment this section. Also, double-check the indentation of the certificate block to ensure it remains valid YAML.

internal/authorization/basic.go (3)

16-18: Add input validation for the prefix parameter.

Consider validating that the prefix parameter is not empty to prevent potential issues with username generation.

 func NewBasicAuthorizer(reader client.Reader, prefix string) *BasicAuthorizer {
+	if prefix == "" {
+		prefix = "internal:"
+	}
 	return &BasicAuthorizer{reader: reader, prefix: prefix}
 }

20-54: Improve code structure and reduce duplication.

The authorization logic for Exporter and Client is very similar. Consider refactoring to reduce duplication and improve readability.

 func (b *BasicAuthorizer) Authorize(
 	ctx context.Context,
 	attributes authorizer.Attributes,
 ) (authorizer.Decision, string, error) {
+	resourceType := attributes.GetResource()
+	if resourceType != "Exporter" && resourceType != "Client" {
+		return authorizer.DecisionDeny, "invalid object kind", nil
+	}
+
+	key := client.ObjectKey{
+		Namespace: attributes.GetNamespace(),
+		Name:      attributes.GetName(),
+	}
+
+	var username string
+	switch resourceType {
+	case "Exporter":
+		var e jumpstarterdevv1alpha1.Exporter
+		if err := b.reader.Get(ctx, key, &e); err != nil {
+			return authorizer.DecisionDeny, "failed to get exporter", err
+		}
+		username = ExporterAuthorizedUsername(&e, b.prefix)
+	case "Client":
+		var c jumpstarterdevv1alpha1.Client
+		if err := b.reader.Get(ctx, key, &c); err != nil {
+			return authorizer.DecisionDeny, "failed to get client", err
+		}
+		username = ClientAuthorizedUsername(&c, b.prefix)
+	}
+
+	if username == attributes.GetUser().GetName() {
+		return authorizer.DecisionAllow, "", nil
+	}
+	return authorizer.DecisionDeny, "", nil
 }

56-70: Reduce duplication in username generation logic.

Both helper functions share similar logic for generating usernames. Consider extracting the common logic into a shared helper function.

+func generateDefaultUsername(prefix, kind, namespace, name string, uid types.UID) string {
+	return prefix + kind + ":" + namespace + ":" + name + ":" + string(uid)
+}
+
 func ClientAuthorizedUsername(c *jumpstarterdevv1alpha1.Client, prefix string) string {
 	if c.Spec.Username == nil {
-		return prefix + "client:" + c.Namespace + ":" + c.Name + ":" + string(c.UID)
+		return generateDefaultUsername(prefix, "client", c.Namespace, c.Name, c.UID)
 	} else {
 		return *c.Spec.Username
 	}
 }
 
 func ExporterAuthorizedUsername(e *jumpstarterdevv1alpha1.Exporter, prefix string) string {
 	if e.Spec.Username == nil {
-		return prefix + "exporter:" + e.Namespace + ":" + e.Name + ":" + string(e.UID)
+		return generateDefaultUsername(prefix, "exporter", e.Namespace, e.Name, e.UID)
 	} else {
 		return *e.Spec.Username
 	}
 }
cmd/main.go (5)

138-142: Consider cert rotation or secure storage.
Creating a self-signed certificate for the OIDC provider is fine for local/demo usage, but for production, consider implementing certificate rotation and secure storage of the private key to adhere to best security practices.


144-153: Ensure the OIDC seed is securely handled.
Passing the signing seed via the “CONTROLLER_KEY” environment variable is convenient but can be risky if the environment is not locked down. For stronger security, ensure it’s loaded from a secure storage service or secrets manager to prevent accidental disclosure.


177-177: Use consistent naming or grouping for signers.
Injecting the same OIDC signer in the ExporterReconciler is consistent with the approach in this codebase. Still, if different reconciliation logic eventually needs distinct signers or issuance policies, consider a more modular approach.


208-215: Check advanced authorization needs.
Using a basic authorizer and metadata attributes is a good step. In the future, consider more granular RBAC or policy-based checks if your system grows in complexity, to avoid large condition blocks.


228-234: Multiple OIDC providers & additional endpoints.
Creating the “OIDCService” improves flexibility. As this expands, consider how multiple upstream OIDC providers might be supported (e.g., additional routes, dynamic issuer). Also ensure proper concurrency handling if any asynchronous calls are introduced.

internal/oidc/token.go (1)

18-33: Gracefully handle failed token authentication.
Your “VerifyOIDCToken” method correctly checks the user’s context for authentication. Verify thorough logging and consider differentiating credentials misconfiguration from actual invalid tokens for better diagnostics.

internal/controller/suite_test.go (1)

125-127: Consider using test-specific constants for OIDC configuration.

The hardcoded values for OIDC configuration should be moved to test constants for better maintainability.

+const (
+    testOIDCIssuer = "https://example.com"
+    testOIDCAudience = "dummy"
+    testOIDCPrefix = "dummy:"
+)
+
 func createExporters(ctx context.Context, exporters ...*jumpstarterdevv1alpha1.Exporter) {
     // ...
-    signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
+    signer, err := oidc.NewSignerFromSeed([]byte{}, testOIDCIssuer, testOIDCAudience, testOIDCPrefix)
internal/controller/lease_controller.go (2)

276-281: Consider adding error handling for policy list retrieval.

While the error is properly wrapped and returned, consider adding a specific error type or constant for policy-related errors to help with error handling in upstream code.

+const (
+    ErrListPolicies = "failed to list exporter access policies"
+)

 var policies jumpstarterdevv1alpha1.ExporterAccessPolicyList
 if err := r.List(ctx, &policies,
     client.InNamespace(lease.Namespace),
 ); err != nil {
-    return fmt.Errorf("reconcileStatusExporterRef: failed to list exporter access policies: %w", err)
+    return fmt.Errorf("%s: %w", ErrListPolicies, err)
 }

297-337: Consider breaking down the nested policy evaluation logic.

The deeply nested policy evaluation logic could be extracted into separate methods for better readability and maintainability.

+func (r *LeaseReconciler) evaluatePolicy(
+    policy jumpstarterdevv1alpha1.ExporterAccessPolicy,
+    exporter jumpstarterdevv1alpha1.Exporter,
+    jclient jumpstarterdevv1alpha1.Client,
+    lease *jumpstarterdevv1alpha1.Lease,
+) ([]struct {
+    Exporter jumpstarterdevv1alpha1.Exporter
+    Policy   jumpstarterdevv1alpha1.Policy
+}, error) {
+    var approved []struct {
+        Exporter jumpstarterdevv1alpha1.Exporter
+        Policy   jumpstarterdevv1alpha1.Policy
+    }
+
+    exporterSelector, err := metav1.LabelSelectorAsSelector(&policy.Spec.ExporterSelector)
+    if err != nil {
+        return nil, fmt.Errorf("failed to convert exporter selector: %w", err)
+    }
+    
+    if !exporterSelector.Matches(labels.Set(exporter.Labels)) {
+        return nil, nil
+    }
+
+    for _, p := range policy.Spec.Policies {
+        for _, from := range p.From {
+            clientSelector, err := metav1.LabelSelectorAsSelector(&from.ClientSelector)
+            if err != nil {
+                return nil, fmt.Errorf("failed to convert client selector: %w", err)
+            }
+            
+            if !clientSelector.Matches(labels.Set(jclient.Labels)) {
+                continue
+            }
+            
+            if p.MaximumDuration != nil && lease.Spec.Duration.Duration > p.MaximumDuration.Duration {
+                continue
+            }
+            
+            approved = append(approved, struct {
+                Exporter jumpstarterdevv1alpha1.Exporter
+                Policy   jumpstarterdevv1alpha1.Policy
+            }{
+                Exporter: exporter,
+                Policy:   p,
+            })
+        }
+    }
+    
+    return approved, nil
+}
internal/controller/lease_controller_test.go (1)

339-341: Consider using test constants for OIDC configuration.

Extract the OIDC test configuration into constants for better maintainability.

+const (
+    testOIDCIssuer = "https://example.com"
+    testOIDCPrefix = "dummy:"
+    testOIDCName   = "dummy"
+)

-signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
+signer, err := oidc.NewSignerFromSeed([]byte{}, testOIDCIssuer, testOIDCName, testOIDCPrefix)
go.mod (1)

3-3: Consider pinning to Go 1.22.0 for better compatibility.

The Go version 1.22.3 is very recent. Consider using 1.22.0 for better compatibility with existing tools and CI systems.

-go 1.22.3
+go 1.22.0
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13591b6 and 012ee1c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (40)
  • PROJECT (1 hunks)
  • api/v1alpha1/authenticationconfiguration_types.go (1 hunks)
  • api/v1alpha1/client_types.go (1 hunks)
  • api/v1alpha1/exporter_types.go (1 hunks)
  • api/v1alpha1/exporteraccesspolicy_types.go (1 hunks)
  • api/v1alpha1/lease_types.go (1 hunks)
  • api/v1alpha1/zz_generated.deepcopy.go (7 hunks)
  • cmd/main.go (6 hunks)
  • cmd/mock/main.go (0 hunks)
  • config/samples/dex.yaml (1 hunks)
  • config/samples/kustomization.yaml (1 hunks)
  • config/samples/v1alpha1_client.yaml (1 hunks)
  • config/samples/v1alpha1_exporteraccesspolicy.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml (1 hunks)
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml (1 hunks)
  • go.mod (3 hunks)
  • internal/authentication/bearer.go (2 hunks)
  • internal/authentication/types.go (1 hunks)
  • internal/authorization/basic.go (1 hunks)
  • internal/authorization/metadata.go (1 hunks)
  • internal/authorization/types.go (1 hunks)
  • internal/config/config.go (1 hunks)
  • internal/controller/client_controller.go (2 hunks)
  • internal/controller/client_controller_test.go (2 hunks)
  • internal/controller/exporter_controller.go (2 hunks)
  • internal/controller/exporter_controller_test.go (2 hunks)
  • internal/controller/lease_controller.go (3 hunks)
  • internal/controller/lease_controller_test.go (2 hunks)
  • internal/controller/suite_test.go (2 hunks)
  • internal/controller/token.go (0 hunks)
  • internal/oidc/config.go (1 hunks)
  • internal/oidc/op.go (1 hunks)
  • internal/oidc/token.go (1 hunks)
  • internal/service/controller_service.go (4 hunks)
  • internal/service/oidc_service.go (1 hunks)
  • internal/service/router_service.go (2 hunks)
💤 Files with no reviewable changes (2)
  • cmd/mock/main.go
  • internal/controller/token.go
🚧 Files skipped from review as they are similar to previous changes (24)
  • config/samples/v1alpha1_client.yaml
  • config/samples/kustomization.yaml
  • api/v1alpha1/authenticationconfiguration_types.go
  • api/v1alpha1/client_types.go
  • internal/authorization/types.go
  • internal/authentication/types.go
  • internal/controller/client_controller.go
  • api/v1alpha1/exporter_types.go
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml
  • config/samples/v1alpha1_exporteraccesspolicy.yaml
  • api/v1alpha1/lease_types.go
  • PROJECT
  • internal/service/router_service.go
  • internal/service/oidc_service.go
  • config/samples/dex.yaml
  • internal/oidc/config.go
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml
  • internal/config/config.go
  • internal/authorization/metadata.go
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml
  • internal/authentication/bearer.go
  • deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml
  • api/v1alpha1/exporteraccesspolicy_types.go
🧰 Additional context used
🪛 YAMLlint (1.35.1)
deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml

[error] 10-10: syntax error: could not find expected ':'

(syntax)

🔇 Additional comments (27)
api/v1alpha1/zz_generated.deepcopy.go (6)

27-27: LGTM! Import for JWT authenticator types.

The import of k8s.io/apiserver/pkg/apis/apiserver/v1beta1 is correctly added to support JWT authenticator types.


30-59: LGTM! Deep copy implementation for AuthenticationConfiguration.

The deep copy functions for AuthenticationConfiguration correctly handle the JWT authenticator slice, ensuring proper deep copying of each element.


66-66: LGTM! Updated deep copy calls for Client and Exporter specs.

The changes correctly use DeepCopyInto for Spec fields instead of direct assignment, ensuring proper deep copying of nested structures.

Also applies to: 192-192


123-127: LGTM! Deep copy implementation for Username field.

The deep copy functions for ClientSpec and ExporterSpec correctly handle the new optional Username field by:

  1. Checking if the field is non-nil
  2. Allocating new string pointer
  3. Copying the string value

Also applies to: 346-350


214-310: LGTM! Deep copy implementation for ExporterAccessPolicy.

The deep copy functions for ExporterAccessPolicy and related types correctly handle:

  1. Object metadata
  2. Spec with exporter selector and policies
  3. Empty status struct

402-416: LGTM! Deep copy implementation for From and Policy types.

The deep copy functions correctly handle:

  1. From type with client selector
  2. Policy type with:
    • Slice of From objects
    • Optional maximum duration field

Also applies to: 530-555

deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml (3)

9-11: YAML Templating and Indentation.

The conditional directive for the timestamp ({{ if .Values.global.timestamp }}) might trigger YAML lint errors because of unexpected whitespace. Consider using Helm’s trimmed delimiters (e.g. {{- if .Values.global.timestamp }} and {{- end }}) to ensure proper indentation and cleaner rendered output.

🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 10-10: syntax error: could not find expected ':'

(syntax)


12-15: Multi-line Authentication Configuration Block.

Ensure that the multi-line string under the "authentication" key correctly represents your intended AuthenticationConfiguration YAML. After Helm renders the template, the embedded configuration should be valid YAML. It might be useful to verify the output with helm template.


40-40: Trailing Incomplete Line.

Line 40 appears to be an incomplete or unintended trailing line. This could lead to YAML parsing errors. Consider removing it or completing the intended configuration if something was meant to be declared.

internal/authorization/basic.go (1)

72-72: LGTM! Good practice for interface compliance.

The compile-time interface compliance check is a good practice to catch potential interface implementation issues early.

cmd/main.go (3)

20-29: Imports look appropriate.
All recently added imports (context, encoding/pem, net, apiserverinstall, authentication, authorization, config, oidc) are relevant to the new OIDC functionality and do not appear to introduce duplication or conflicts.

Also applies to: 43-47


155-172: Validate external dependencies and configuration data.
Loading OIDC configuration via “LoadConfiguration” is a good approach. However, ensure that any external calls within this configuration process are robustly error-checked and that unexpected/missing fields are handled gracefully.


185-185: Maintain uniform design for all reconcilers.
Similarly, extending the same signer to the ClientReconciler is coherent. Just ensure separate domain boundaries are enforced if future updates require different trust contexts or specialized signers.

internal/oidc/token.go (2)

35-69: Verify the case-sensitivity for resource checks.
The code expects attrs.GetResource() == "Client". Some Kubernetes-based resource names are lowercase or plural. Confirm that your resource naming here matches the actual CRD naming to avoid mismatch errors in production.


71-105: Enforce consistent naming for exporters.
Same concern regarding resource naming: the logic checks "Exporter" but your CRD might be “exporters” or a different pattern. Confirm correctness.

internal/controller/exporter_controller.go (3)

40-40: LGTM: Signer field addition.

The addition of the OIDC Signer field is correctly implemented.


46-46: LGTM: RBAC permission for ExporterAccessPolicy.

The RBAC permission is correctly scoped to read-only operations (get;list;watch) for ExporterAccessPolicy resources.


157-158: Verify token generation with proper prefix.

The token generation uses the authorized username with the signer's prefix, but let's verify the prefix format matches the PR objectives.

internal/controller/client_controller_test.go (1)

82-83: Use shared test constants for OIDC configuration.

The hardcoded OIDC configuration values should use the same test constants suggested for suite_test.go.

internal/controller/exporter_controller_test.go (1)

82-83: Use shared test constants for OIDC configuration.

The hardcoded OIDC configuration values should use the same test constants suggested for suite_test.go.

internal/controller/lease_controller.go (3)

271-274: LGTM! Well-structured policy data type.

The struct definition for approved exporters with policy information is clean and well-organized.


283-296: LGTM! Sensible default policy handling.

The code correctly handles the case when no policies exist by approving all available exporters with default policy values.


353-358: LGTM! Clear policy application.

The code correctly applies the selected policy's priority and spot access settings to the lease status.

internal/service/controller_service.go (3)

64-67: LGTM! Well-structured authentication and authorization fields.

The ControllerService struct properly integrates authentication and authorization components.


70-78: LGTM! Clean authentication method implementation.

The authentication methods are well-implemented using the OIDC verifier.

Also applies to: 80-88


209-211: LGTM! Proper authentication check in ListExporters.

The ListExporters method correctly verifies client authentication before proceeding.

go.mod (1)

6-8: Verify OIDC package versions for security.

The OIDC-related packages are critical for security. Let's verify their versions for any known vulnerabilities.

Also applies to: 14-14

Copy link
Member

@mangelajo mangelajo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, RBAC works, with some limitations but we can follow up.

Internal OIDC works, I haven't tested the external one yet.

Let's iterate

@mangelajo mangelajo merged commit 755620e into main Feb 12, 2025
6 checks passed
@NickCao NickCao deleted the oidc-design branch February 12, 2025 16:59
authentication: |
apiVersion: jumpstarter.dev/v1alpha1
kind: AuthenticationConfiguration
# jwt:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NickCao can you write some docs on this config, and add the helm values to be able to inject this into place?

May be just a value that allows to pum in the whole authentication configuration?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #85

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants