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: limit controller access to control plane secrets #2522

Closed
wants to merge 23 commits into from

Conversation

fykaa
Copy link
Contributor

@fykaa fykaa commented Sep 12, 2024

Issue Reference:
Closes: #2448

This PR limits the kargo-controller's access to project-specific secrets by creating a ClusterRole (kargo-controller-secrets-readonly) with read-only permissions. Role bindings are dynamically created per project and removed upon project deletion and this ensures least-privileged access for controllers.

Impact:
Tighter scoping of controller access to secrets, more security for sharded controllers.

@fykaa fykaa self-assigned this Sep 12, 2024
@fykaa fykaa requested a review from a team as a code owner September 12, 2024 15:23
Copy link

netlify bot commented Sep 12, 2024

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

Name Link
🔨 Latest commit bb8bdb2
🔍 Latest deploy log https://app.netlify.com/sites/docs-kargo-akuity-io/deploys/6707d2626443e70008539820
😎 Deploy Preview https://deploy-preview-2522.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

@fykaa, this looks like a good start, but as you know, we've encountered some scope creep that is described in detail here: #2448 (comment)

ctx context.Context,
project *kargoapi.Project,
) error {
const roleBindingName = "kargo-controller-secrets-readonly"
Copy link
Member

Choose a reason for hiding this comment

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

Interesting. In my head, each Project was going to have a RoleBinding per controller SA.

You're actually creating or updating a single RoleBinding and making sure it has all the controller SAs listed as its subjects.

On some level, I quite like that, but I have a worry this will be more complicated than anticipated once you get into part two of this PR...

Remember that we also need to deal with creating/removing bindings as new controller SAs come and go. Once we start doing that, you start to run into possibilities like a new Project and a new SA being reconciled concurrently and racing to update the same binding.

One tempting solution to that problem is to patch the RoleBinding instead of updating it, but patching lists can be quite difficult.

In the end, it may be more straightforward if every Project has one RoleBinding per controller SA. I feel that would lead to fewer conflicts and race conditions.

But I also want @hiddeco's opinion on this matter.

Copy link
Member

Choose a reason for hiding this comment

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

Note that apart from this, this PR is off to a very good start.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your detailed feedback! You raised some excellent points that I hadn’t fully considered, particularly around the potential for race conditions and the implications of managing rolebinding for multiple SAs.

As it stands, the implementation creates or updates a single RoleBinding that grants read-only access to Secrets for all controller ServiceAccounts associated with a project. This approach was straightforward for me initially, but I now see that it could lead to complications, especially when new controller SAs are added or existing ones are removed (I am yet to add the SA reconciler package)

I need to explore the approach you mentioned regarding patching the RoleBinding (instead of updating it).
As of now, one RoleBinding per controller ServiceAccount may simplify the management of permissions and reduce the risk of conflicts. I guess I need to understand the architecture better, that would enable me to propose a more robust solution that aligns with our overall design.

Copy link
Member

Choose a reason for hiding this comment

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

Let's wait to hear from @hiddeco before making any changes. He usually has the best insight into avoiding races in controllers.

Copy link
Contributor

Choose a reason for hiding this comment

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

As the list of subjects is a list of primitives, there is no support for merging them in Kubernetes. Besides this, even if there would be support for it, we would not be in control over defining this behavior as it is part of the CustomResourceDefinition (of which we are not the author) using patchMergeKey.

Given this, there are only a few options to avoid any concurrency issues:

  1. You put one reconciler in control of the reconciliation of the RoleBinding, while making it react to both Project and ServiceAccount changes (using carefully crafted watches). This way, any race condition is eliminated, as any change of interest will trigger a new observation taking the whole state of the world into account.
  2. You issue a patch in combination with an optimistic lock. When this results in a conflict (due to A winning from B), you retry it by re-fetching the resources and all the bits you are interested in, to then attempt to make the change again.

The main issue with option 2 is that both reconcilers only update the bits they care about, causing a potential blind spot. This means each reconciler lacks full visibility into changes made by the other, potentially still leading to unintended overwrites or inconsistent states in the RoleBinding. This is why a single reconciler with a holistic view (as in option 1) would be the more reliable option.

Copy link
Member

@krancour krancour Sep 16, 2024

Choose a reason for hiding this comment

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

You put one reconciler in control of the reconciliation of the RoleBinding

ikd if this is a viable option. It's not the RoleBindings that need to be reconciled.

RoleBindings need to be updated or created/removed in response to both Projects and controller SAs being added/removed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To confirm:

Are we aiming to create a reconciler specifically for RoleBindings that would react to changes in Projects and ServiceAccounts (the option 1)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To avoid race conditions: we'll go with creating one RoleBinding per Service Account. This way, each SA gets its own RoleBinding in the Project namespace.
(also solve the problem: merging a list of primitives/ subjects in k8s).

This is simple, with more objects to manage, but less complexity and potential races.

Copy link

netlify bot commented Oct 3, 2024

Deploy Preview for docs-kargo-io canceled.

Name Link
🔨 Latest commit f57983a
🔍 Latest deploy log https://app.netlify.com/sites/docs-kargo-io/deploys/670fd7680d5f9d0008169dbc

@krancour krancour marked this pull request as draft October 3, 2024 12:10
@krancour krancour self-assigned this Oct 7, 2024
@fykaa
Copy link
Contributor Author

fykaa commented Oct 9, 2024

This PR is yet to hit the ground running with some tests and refactoring logic. RoleBinding Creation logic is completed. watches.go currently handles RoleBinding Deletion when ServiceAccount is Deleted OR ServiceAccount's controller label is removed.

Signed-off-by: Faeka Ansari <[email protected]>
Signed-off-by: Faeka Ansari <[email protected]>
Signed-off-by: Faeka Ansari <[email protected]>
Signed-off-by: Faeka Ansari <[email protected]>
Signed-off-by: Faeka Ansari <[email protected]>

fix nits

Signed-off-by: Faeka Ansari <[email protected]>
@krancour
Copy link
Member

Closing in favor of #2761

@krancour krancour closed this Oct 16, 2024
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.

feat: limit controller access to control plane secrets
3 participants