-
Couldn't load subscription status.
- Fork 3
Authorization for apiserver #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: YaoZengzeng <[email protected]>
Signed-off-by: YaoZengzeng <[email protected]>
Signed-off-by: YaoZengzeng <[email protected]>
Signed-off-by: YaoZengzeng <[email protected]>
Summary of ChangesHello @YaoZengzeng, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant architectural change to the pico-apiserver's authorization system. It transitions from a rudimentary JWT-based authentication to a robust, Kubernetes-native Service Account TokenReview mechanism. This shift ensures that all API operations, particularly those involving Kubernetes resource management like sandbox lifecycles, are performed with the explicit permissions of the requesting user's Service Account and within their specific namespace. This change not only strengthens the security posture but also lays the groundwork for more sophisticated multi-tenant environments. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request is a significant improvement, transitioning the apiserver from a simple JWT secret to a robust Kubernetes-native authentication model using Service Account tokens and the TokenReview API. The shift to a caller-permission-based model, where actions are performed with the user's identity in their own namespace, is a great security enhancement. The code is well-structured, and the new example client with its detailed README is very helpful. My review includes a few suggestions to enhance the robustness of token parsing and context value extraction to make the new implementation even more solid.
| if authToken == "" { | ||
| log.Println("⚠️ WARNING: API_TOKEN environment variable not set") | ||
| log.Println(" Attempting to proceed without authentication token") | ||
| log.Println(" Set API_TOKEN to authenticate with the API server") | ||
| log.Println() | ||
| } else { | ||
| log.Println("✅ API authentication token loaded from environment") | ||
| log.Println() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the server now mandates an authentication token, the client will fail with a 401 Unauthorized error if API_TOKEN is not set. Instead of printing a warning and proceeding, it would provide clearer and more immediate feedback to the user if the client exits with a fatal error. This makes the example client's behavior consistent with the new server requirements.
if authToken == "" {
log.Fatalf("❌ ERROR: API_TOKEN environment variable not set. Please set it to a valid service account token.")
}
log.Println("✅ API authentication token loaded from environment")
log.Println()| saParts := strings.Split(serviceAccount, ":") | ||
| var namespace, serviceAccountName string | ||
| if len(saParts) == 4 && saParts[0] == "system" && saParts[1] == "serviceaccount" { | ||
| namespace = saParts[2] | ||
| serviceAccountName = saParts[3] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parsing the service account username by splitting on : and checking the length can be fragile. A more robust approach is to use strings.CutPrefix to check for the standard prefix (system:serviceaccount:) and then split the remainder. This makes the code's intent clearer and less prone to errors if the username format has unexpected variations.
| saParts := strings.Split(serviceAccount, ":") | |
| var namespace, serviceAccountName string | |
| if len(saParts) == 4 && saParts[0] == "system" && saParts[1] == "serviceaccount" { | |
| namespace = saParts[2] | |
| serviceAccountName = saParts[3] | |
| } | |
| const serviceAccountUsernamePrefix = "system:serviceaccount:" | |
| var namespace, serviceAccountName string | |
| if username, ok := strings.CutPrefix(serviceAccount, serviceAccountUsernamePrefix); ok { | |
| if parts := strings.SplitN(username, ":", 2); len(parts) == 2 { | |
| namespace = parts[0] | |
| serviceAccountName = parts[1] | |
| } | |
| } |
| userToken, _ := r.Context().Value(contextKeyUserToken).(string) | ||
| userNamespace, _ := r.Context().Value(contextKeyNamespace).(string) | ||
| serviceAccount, _ := r.Context().Value(contextKeyServiceAccount).(string) | ||
|
|
||
| if userToken == "" || userNamespace == "" { | ||
| respondError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unable to extract user credentials") | ||
| return | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more idiomatic in Go to check the ok boolean returned from a type assertion when retrieving values from a context. While the current code works because a failed assertion for a string results in an empty string, explicitly checking ok makes the intent clearer and the code more robust.
| userToken, _ := r.Context().Value(contextKeyUserToken).(string) | |
| userNamespace, _ := r.Context().Value(contextKeyNamespace).(string) | |
| serviceAccount, _ := r.Context().Value(contextKeyServiceAccount).(string) | |
| if userToken == "" || userNamespace == "" { | |
| respondError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unable to extract user credentials") | |
| return | |
| } | |
| userToken, ok1 := r.Context().Value(contextKeyUserToken).(string) | |
| userNamespace, ok2 := r.Context().Value(contextKeyNamespace).(string) | |
| serviceAccount, _ := r.Context().Value(contextKeyServiceAccount).(string) | |
| if !ok1 || !ok2 || userToken == "" || userNamespace == "" { | |
| respondError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unable to extract user credentials") | |
| return | |
| } |
| userToken, _ := r.Context().Value(contextKeyUserToken).(string) | ||
| userNamespace, _ := r.Context().Value(contextKeyNamespace).(string) | ||
| serviceAccount, _ := r.Context().Value(contextKeyServiceAccount).(string) | ||
|
|
||
| if userToken == "" || userNamespace == "" { | ||
| respondError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unable to extract user credentials") | ||
| return | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the handleCreateSession handler, it's more idiomatic and robust to check the ok boolean from the type assertion when extracting values from the request context. This improves code clarity and ensures that the absence of a value is handled explicitly.
| userToken, _ := r.Context().Value(contextKeyUserToken).(string) | |
| userNamespace, _ := r.Context().Value(contextKeyNamespace).(string) | |
| serviceAccount, _ := r.Context().Value(contextKeyServiceAccount).(string) | |
| if userToken == "" || userNamespace == "" { | |
| respondError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unable to extract user credentials") | |
| return | |
| } | |
| userToken, ok1 := r.Context().Value(contextKeyUserToken).(string) | |
| userNamespace, ok2 := r.Context().Value(contextKeyNamespace).(string) | |
| serviceAccount, _ := r.Context().Value(contextKeyServiceAccount).(string) | |
| if !ok1 || !ok2 || userToken == "" || userNamespace == "" { | |
| respondError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unable to extract user credentials") | |
| return | |
| } |
No description provided.