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

✨ Add rudimentary user gating #3

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Conversation

csams
Copy link
Contributor

@csams csams commented Nov 8, 2022

Summary

The user gate is a rudimentary authorization filter that controls which authenticated users are authorized to make requests against CPS. It is dynamically configured by a secret containing rules in the kcp-system namespace of the root workspace.

This filter is meant to be used along with another system that dynamically updates the secret during a user sign-up workflow ahead of general availability. It should not be used in its current form after a more general mechanism provided by CIAM is in place.

Follow on PRs will change the filter so that it injects groups and/or some form of entitlement information into the user info (after parsed from the JWT), which should dovetail into how we want CIAM to control whether users are allowed to access CPS.

The user gate is a rudimentary authorization filter that controls which
authenticated users are authorized to make requests against CPS. It is
dynamically configured by a secret containing rules in the kcp-system
namespace of the root workspace.

This filter is meant to be used along with another system that
dynamically updates the secret during a user sign-up workflow ahead of
general availability. It should not be used after a more general
mechanism provided by CIAM is in place.
@openshift-ci openshift-ci bot requested review from hardys and ncdc November 8, 2022 20:36
@openshift-ci
Copy link

openshift-ci bot commented Nov 8, 2022

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign stevekuznetsov for approval by writing /assign @stevekuznetsov in a comment. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sttts
Copy link
Contributor

sttts commented Nov 9, 2022

/assign @hardys

@hardys
Copy link

hardys commented Nov 9, 2022

Summary of some previous discussions from the upstream PR

There were concerns and suggestions from @sttts and @s-urbaniak about the proxy reading a secret from the root workspace for this.

Personally I agree and think a rules file managed as part of the proxy deployment would be much simpler, but @csams explained that there is a requirement for systems which don't have access to the proxy deployment to update the rules.

resyncPeriod = 10 * time.Minute

UserGateNamespace = "kcp-system"
UserGateResource = "usergate.kcp.dev"
Copy link

Choose a reason for hiding this comment

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

Should we add flags for the namespace and secret name as discussed upstream?

Not a blocker but it seems like it might be a mitigation for the reading-root-secrets concern if we can ensure RBAC limits access allowed from the proxy?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, will do.

@hardys
Copy link

hardys commented Nov 9, 2022

/cc @s-urbaniak

@hardys
Copy link

hardys commented Nov 9, 2022

We'll need to merge #4 then rebase this so we can get the prow jobs to run

@hardys
Copy link

hardys commented Nov 9, 2022

@csams please can you rebase now #4 merged, note you'll need to add the new pkg dir to the Dockerfile

Logs *logs.Options
}

func NewOptions() *Options {
Copy link
Contributor

Choose a reason for hiding this comment

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

global comment: please godoc everything that's exported

}

func (o *UserGate) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&o.Enabled, "enable-user-gating", false, "Enabled user gating.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
fs.BoolVar(&o.Enabled, "enable-user-gating", false, "Enabled user gating.")
fs.BoolVar(&o.Enabled, "enable-user-gating", o.Enabled, "Enable user gating.")


func (o *UserGate) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&o.Enabled, "enable-user-gating", false, "Enabled user gating.")
fs.StringVar(&o.DefaultRulesFile, "user-gating-rules", "", "Provide a YAML file containing user gating rules.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
fs.StringVar(&o.DefaultRulesFile, "user-gating-rules", "", "Provide a YAML file containing user gating rules.")
fs.StringVar(&o.DefaultRulesFile, "user-gating-rules", o.DefaultRulesFile, "Path to a YAML file containing user gating rules.")

"os"
"time"

coreinformers "github.com/kcp-dev/client-go/informers"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
coreinformers "github.com/kcp-dev/client-go/informers"
kcpinformers "github.com/kcp-dev/client-go/informers"

"time"

coreinformers "github.com/kcp-dev/client-go/informers"
coreclient "github.com/kcp-dev/client-go/kubernetes"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
coreclient "github.com/kcp-dev/client-go/kubernetes"
kcpkubernetesclientset "github.com/kcp-dev/client-go/kubernetes"

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm seeing both kcpkubernetesclientset and kcpkubernetesclient in the kcp code - we should standardize. cc @stevekuznetsov

"sigs.k8s.io/yaml"
)

type RuleSetGetter func() (*RuleSet, bool)
Copy link
Contributor

Choose a reason for hiding this comment

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

Define this where it's used, i.e. not in this package


if rules, found := a.GetRuleSet(); found {
if rules.UserNameIsAllowed(info.GetName()) {
return authorizer.DecisionAllow
Copy link
Contributor

Choose a reason for hiding this comment

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

Call kaudit.AddAuditAnnotations before returning any/all decisions to record why

})
}

// Authorize whether the user is allowed to make the request
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Authorize whether the user is allowed to make the request
// Authorize determines whether the user is allowed to make the request.

}

decision := gate.Authorize(ctx, u)
if decision != authorizer.DecisionDeny {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to allow NoOpinion to be allowed in? cc @s-urbaniak

Copy link

@hardys hardys Nov 10, 2022

Choose a reason for hiding this comment

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

This is an interesting point, it seems like we should perhaps only have allowed_user_names then deny all other users, or else this isn't really a gate to prevent access by undefined random users?


func (c *Controller) process(ctx context.Context, key string) error {
logger := klog.FromContext(ctx)
logger.WithValues("key", key).Info("updating usergate secret")
Copy link
Contributor

Choose a reason for hiding this comment

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

We're not really updating the secret?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed to "updating usergate ruleset"

GetRuleSet ruleset.RuleSetGetter
}

func WithUserGating(delegate http.Handler, failed http.Handler, getRuleSet ruleset.RuleSetGetter) http.Handler {
Copy link

Choose a reason for hiding this comment

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

I think this file naming would be more consistent with the upstream proxy if it was e.g pkg/proxy/filters/usergating.go ?

// Authorize whether the user is allowed to make the request
func (a *UserGate) Authorize(ctx context.Context, info user.Info) authorizer.Decision {
logger := klog.FromContext(ctx).WithValues("component", "user-gating")
logger.V(1).Info("checking auth", "user", info.GetName())
Copy link

Choose a reason for hiding this comment

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

This logging is likely to be too noisy I think, should probably either be removed or put at a higher loglevel?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll remove it in favor of ncdc's kaudit.AddAuditAnnotations feedback.

"github.com/kcp-dev/client-go/informers"
tenancyv1alpha1 "github.com/kcp-dev/kcp/pkg/apis/tenancy/v1alpha1"

v1 "k8s.io/api/core/v1"
Copy link

Choose a reason for hiding this comment

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

I think aliasing this to corev1 would be more consistent with upstream?

ctx := r.Context()
u, ok := request.UserFrom(ctx)
if !ok {
delegate.ServeHTTP(w, r)
Copy link

Choose a reason for hiding this comment

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

Similar to the comment below, do we want this to be allow-by-default or should this return failed if we can't identify the user?

Copy link
Contributor Author

@csams csams Nov 11, 2022

Choose a reason for hiding this comment

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

Do all authentication methods give a user.Info? Even if so, can we still gate them correctly? Thinking about service account tokens generated in workspaces.

Copy link

Choose a reason for hiding this comment

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

From my testing yes, I've tested with token-file, SA token, client-cert and OIDC and they all result in a user.Info containing a username when authentication is successful

Even Anonymous auth sets a username AFAICS (although we probably don't want to enable that at the proxy)

Copy link

Choose a reason for hiding this comment

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

We'll need to test to confirm, but from my SA testing (where I just used the default SA token from the secret in the root workspace), I think we'll need to have a rule like ^system:serviceaccount:.*:*$ or similar

@openshift-merge-robot
Copy link
Contributor

@csams: PR needs rebase.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@openshift-ci
Copy link

openshift-ci bot commented Dec 8, 2022

@csams: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/lint da82edb link true /test lint
ci/prow/test da82edb link true /test test
ci/prow/boilerplate da82edb link true /test boilerplate
ci/prow/images da82edb link true /test images

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

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.

5 participants