Skip to content
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

feat: allow mapping SSO context to ServiceAccount #1148

Merged
merged 10 commits into from
Nov 27, 2023
Merged

feat: allow mapping SSO context to ServiceAccount #1148

merged 10 commits into from
Nov 27, 2023

Conversation

devholic
Copy link
Contributor

#1102

This commit maps SSO context (user information) to ServiceAccount to authorize user to access Kubernetes API with the matching ServiceAccount's permission.

@jessesuen For now, this PR doesn't support glob pattern as annotation values. IMHO, it would be better to make users to configure Auth-related settings explicitly - since it will not allow unintended subjects by default. WDYT?

Copy link

netlify bot commented Nov 20, 2023

Deploy Preview for docs-kargo-akuity-io ready!

Name Link
🔨 Latest commit 8ae2cf7
🔍 Latest deploy log https://app.netlify.com/sites/docs-kargo-akuity-io/deploys/6564912714dcdb00086c8d8c
😎 Deploy Preview https://deploy-preview-1148.kargo.akuity.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@krancour
Copy link
Member

👀

@krancour
Copy link
Member

@devholic, overall this looks very close to what we need. I left just a few comments on how to do this with fewer permissions and fewer access reviews.

This was referenced Nov 20, 2023
@krancour krancour removed their assignment Nov 21, 2023
Sunghoon Kang added 2 commits November 22, 2023 14:03
#1102

This commit maps SSO context (user information) to ServiceAccount to
authorize user to access Kubernetes API with the matching
ServiceAccount's permission.

Signed-off-by: Sunghoon Kang <hoon@akuity.io>
Signed-off-by: Sunghoon Kang <hoon@akuity.io>
@devholic
Copy link
Contributor Author

@krancour

  • Update getAuthorizedClient to constrain namespace on namspaced resources
  • Add rbac.kargo.akuity.io/fallback-service-account annotation to mark fallback accounts in system namespace

@devholic devholic requested a review from krancour November 22, 2023 05:08
Copy link

codecov bot commented Nov 22, 2023

Codecov Report

Attention: 178 lines in your changes are missing coverage. Please review.

Comparison is base (72b21f7) 49.49% compared to head (8ae2cf7) 48.81%.
Report is 2 commits behind head on main.

Files Patch % Lines
internal/api/kubernetes/client.go 28.57% 88 Missing and 2 partials ⚠️
internal/api/option/auth.go 13.20% 45 Missing and 1 partial ⚠️
internal/kubeclient/indexer.go 0.00% 40 Missing ⚠️
internal/api/option/option.go 0.00% 1 Missing ⚠️
internal/api/server.go 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1148      +/-   ##
==========================================
- Coverage   49.49%   48.81%   -0.68%     
==========================================
  Files         115      115              
  Lines        8373     8536     +163     
==========================================
+ Hits         4144     4167      +23     
- Misses       4081     4218     +137     
- Partials      148      151       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@krancour
Copy link
Member

@devholic looking good. I have two changes I want to propose:

  1. All of this new logic for selecting SAs that the user is mapped to, followed by SARs until one of those SAs turns out to be suitable is all going to be repeated for every call the API server makes to the k8s client. Since some API server endpoints make many calls to the k8s client, this can end up being pretty inefficient.

    There is no getting around doing SARs just-in-time, unfortunately, but we can find all the SAs that the user is mapped to just once per request to the API server by doing it as part of the authentication process. We can include the full set of SAs the user is mapped to as a new field in the user.Info struct that gets bound to the request context. (We don't need the full SA structs -- just their namespace and name.)

    Besides not having to work out what the mappings are multiple times per request to the API, this approach would also give us the opportunity to preempt the entire request and return a 403 if the user isn't mapped any SAs at all.

  2. I wasn't imagining that we fall back on a completely static set up SAs in the kargo namespace when no mappings yield SAs for the user in project namespace(s). I was imagining the mappings in the kargo namespace would still be dynamic.

    i.e. I'd rather see rbac.kargo.akuity.io/fallback-service-account go away and have the SAs in that NS just play by the same rules as the projects using the rbac.kargo.akuity.io/sub and rbac.kargo.akuity.io/groups annotations.

    Why it matters:

    With the static set of SAs annotated with rbac.kargo.akuity.io/fallback-service-account, there would be no way for a Kargo admin to designate a broad set of permissions only for specific users or groups. But by playing by the same rules as in the other namespaces, this possibility exists.

Sunghoon Kang added 2 commits November 23, 2023 09:19
Signed-off-by: Sunghoon Kang <hoon@akuity.io>
Signed-off-by: Sunghoon Kang <hoon@akuity.io>
@devholic
Copy link
Contributor Author

@krancour

  • Edited AuthInterceptor to fetch ServiceAccount candidates and use them when submitting Access Review
  • Use sub/groups annotations instead of fallback annotations in kargo namespace

@krancour
Copy link
Member

@devholic this is looking awesome. I made some more suggestions, but I think they're mostly small things -- mainly aimed at fewer/tighter loops.

@krancour
Copy link
Member

For now, this PR doesn't support glob pattern as annotation values. IMHO, it would be better to make users to configure Auth-related settings explicitly - since it will not allow unintended subjects by default. WDYT?

💯 agree with @devholic.

What stops someone from coming along with a new/unanticipated group and thereby acquiring permissions there were not meant to have?

I think if we really want that, we need to put a lot of deliberate thought into how to prevent such a scenario.

I think it's right to leave it out for now.

#1148 (review)

- Reuse internal client
- Rename getUserClient to reviewSubjectAccess
- Use `types.NamespacedName` for ServiceAccounts in `UserInfo`

Signed-off-by: Sunghoon Kang <hoon@akuity.io>
@devholic
Copy link
Contributor Author

Thanks for the review @krancour !

I hope 60250d1 will be the final fix 😅 - but feel free to let me know if any changes required 🙏

The commit contains:

  • Reuse internal client
  • Rename getUserClient to reviewSubjectAccess
  • Use types.NamespacedName for ServiceAccounts in UserInfo

Comment on lines 292 to 296
for ns, names := range accounts {
for name := range names {
res[ns] = append(res[ns], name)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

How much do we really need this loop?

Can we just return the map[string]map[types.NamespacedName]struct{} instead of returning map[string][]types.NamespacedName?

I think that would be ok, because I don't think order really matters when we use this information for authz decisions later on.

wdyt?

Comment on lines 32 to 33
ServiceAccountsByGroupIndexField = "group"
ServiceAccountsBySubjectIndexField = "subject"
Copy link
Member

Choose a reason for hiding this comment

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

Tiniest nit: Should these be plural "groups" and "subjects" because I think both of these indices can end up being mulit-valued.

Copy link
Member

Choose a reason for hiding this comment

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

Non-blocking.

Comment on lines +271 to +285
func indexServiceAccountsByRBACGroups(obj client.Object) []string {
sa := obj.(*corev1.ServiceAccount) // nolint: forcetypeassert
rawGroups := strings.TrimSpace(sa.GetAnnotations()[kargoapi.AnnotationKeyRBACGroups])
if rawGroups == "" {
return nil
}
groups := strings.Split(rawGroups, ",")
refinedGroups := make([]string, 0, len(groups))
for _, g := range groups {
if group := strings.TrimSpace(g); group != "" {
refinedGroups = append(refinedGroups, group)
}
}
return refinedGroups
}
Copy link
Member

Choose a reason for hiding this comment

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

Had a thought about all the new indexing functions...

Should we be validating that the SA's namespace is indeed a Kargo project?

We either have to do that when building the index or after using the index.

I think it's cheaper to do it when building the index, because this function will fire only when a SA is modified and I think that should happen a lot less frequently than when we use the indices during the authn process.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@krancour Unless the IndexerFunc allows us to return error, I think it would be better to filter after listing service accounts for fail-safety. WDYT?

@krancour
Copy link
Member

Looking fantastic. I think there are two small things to give some consideration to, and one very tiny nit that you can choose to deal with or not.

Sunghoon Kang added 3 commits November 27, 2023 15:39
Signed-off-by: Sunghoon Kang <hoon@akuity.io>
#1148 (comment)

Signed-off-by: Sunghoon Kang <hoon@akuity.io>
Signed-off-by: Sunghoon Kang <hoon@akuity.io>
Copy link
Member

@krancour krancour left a comment

Choose a reason for hiding this comment

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

🔥 LGTM 🔥

Thanks for all the hard for on this @devholic!

@devholic devholic added this pull request to the merge queue Nov 27, 2023
Merged via the queue into main with commit a782496 Nov 27, 2023
12 of 14 checks passed
@devholic devholic deleted the hoon/#1102 branch November 27, 2023 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants