feat: Add proxy-authorization flag to login command#5229
feat: Add proxy-authorization flag to login command#5229onasser1 wants to merge 3 commits intoakuity:mainfrom
Conversation
Signed-off-by: Omar Nasser <omarnasserjr@gmail.com>
✅ Deploy Preview for docs-kargo-io ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5229 +/- ##
==========================================
+ Coverage 55.04% 55.96% +0.91%
==========================================
Files 401 404 +3
Lines 36017 29710 -6307
==========================================
- Hits 19827 16628 -3199
+ Misses 15227 12119 -3108
Partials 963 963 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@onasser1 at a glance, this looks pretty good. I will review more thoroughly as soon as I am able. From #3537:
I really don't know much about GCP IAP, and it's not a rabbit hole I am willing to fall into right now, but as I mentioned in #3537, the Proxy-Authorization header is formally defined in rfc7235. It's generally useful and not only an IAP thing. If the header's verifiably being set, I think the objective has been achieved. |
Although, now that I've said this, I notice you're allowing this to be specified at login time and you are persisting it in Kargo's config file, but I see you're not yet incorporating this value into into the |
Signed-off-by: Omar Nasser <omarnasserjr@gmail.com>
| credential string, | ||
| proxyCredential string, | ||
| insecureTLS bool, |
There was a problem hiding this comment.
As the number of optional details here grows, it's probably better that we think about introducing a ClientOptions struct. The signature would change to:
func GetClient(
serverAddress string,
opts *ClientOptions,
) svcv1alpha1connect.KargoServiceClient
It should be tolerant of nil options.
There was a problem hiding this comment.
I was going to suggest functional options here before I saw your comment.
e.g. WithCredentials, WithProxyCredentials
func GetClient(
serverAddress string,
opts ...ClientOption,
) svcv1alpha1connect.KargoServiceClient
Thoughts?
There was a problem hiding this comment.
It's doable, of course, but I'm not sure I like it here. If you look at what the GetClient() function is configuring, it's an http.Client as well as options for the svcv1alpha1connect.KargoServiceClient. Options functions would need to pass around both of these. It's not pretty and if you wanted to tidy it up, you'd need to create some kind of options struct that gets passed around instead, and then we're more or less back to where we started. And because we're not really developing an API here, but simply trying to stop an argument list for something used internally from sprawling, I think we can skip this. It's tidy enough to bind flags directly to option struct fields and call it a day.
|
@onasser1 this seems to be on the right track. One small bit of feedback: https://github.com/akuity/kargo/pull/5229/files#r2466991433 @fuskovic do you mind giving this a review after the above is accounted for? @steve-marmalade, this feature was your request. If you were willing to test it against your use case, it would be a big help. |
| return svcv1alpha1connect.NewKargoServiceClient(httpClient, serverAddress) | ||
| } | ||
|
|
||
| if credential != "" && proxyCredential == "" { |
There was a problem hiding this comment.
All the logic that follows this can be greatly simplified to something like this:
interceptor := &authInterceptor{}
if credential == "" {
interceptor.credential = credential
}
if proxyCredential == "" {
interceptor.proxyCredential = proxyCredential
}
return svcv1alpha1connect.NewKargoServiceClient(
httpClient,
serverAddress,
connect.WithClientOptions(connect.WithInterceptors(interceptor)),
)Signed-off-by: Omar Nasser <omarnasserjr@gmail.com>
| Credentials string | ||
| ProxyCredentials string | ||
| InsecureTLS bool |
There was a problem hiding this comment.
With your naming ClientOptions, did you mean to create a totally new struct? I thought the same and tried to create a new ClientOptions struct but go linting has complained about the naming.
pkg/cli/client/client.go:22:6: exported: type name will be used as client.ClientOptions by other packages, and that stutters; consider calling this Options (revive)
type ClientOptions struct {}
So I tried to use the existent Options struct and add the credential fields. I was afraid if this would cause a break to the dependencies, but it didn't.
Shall we go with the Options struct or I have to create a new struct with a different name than ClientOptions?
@fuskovic
There was a problem hiding this comment.
I forgot an Options struct already existed in this package was all.
There was a problem hiding this comment.
The way you're currently doing things (this particular bit) is good.
| if o.UseProxyAuth { | ||
| for o.ProxyAuthCredentials == "" { | ||
| prompt := &survey.Password{ | ||
| Message: "Proxy Authorization token", | ||
| } | ||
| if err = survey.AskOne(prompt, &o.ProxyAuthCredentials); err != nil { | ||
| return err | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
You really ought to be collecting this information before any login attempt, because if credentials for a proxy are required, they're require during login as well.
There was a problem hiding this comment.
Any login function that interacts with the Kargo API server will need to use this.
adminLogin() would need to use it for its interactions with the API server's "public config" endpoint as well as the login endpoint (where password is exchanged for a bearer token).
ssoLogin() would need to use it for its interactions with the API server's "public config" endpoint and if the OIDC identity provider's issuer URL has the same hostname as the Kargo API server, it would need to be used for all interactions with the identity provider as well. This is a rather complex thing to do.
I think kubeconfigLogin() probably doesn't require any modifications since it has not interactions with the Kargo API server.
|
After looking at this in greater depth, it's much more complicated than I initially anticipated. Combining that with its security implications, if this gets merged, I'd like to personally be the one signing off on it. Please note, however, that this remains a low priority. |
|
|
||
| func setProxyAuthHeader(header http.Header, proxyCred string) { | ||
| if proxyCred != "" { | ||
| header.Set(proxyAuthHeaderKey, fmt.Sprintf("Bearer %s", proxyCred)) |
There was a problem hiding this comment.
I'm not sure it's a given that the credential is a bearer token.
There was a problem hiding this comment.
So what value for the <auth-scheme> should we use here?
|
See #3537 (comment) |
Fixes #3537