Beispiel #1
0
// NewRequestAuthenticator creates an http handler that tries to authenticate the given request as a user, and then
// stores any such user found onto the provided context for the request. If authentication fails or returns an error
// the failed handler is used. On success, handler is invoked to serve the request.
func NewRequestAuthenticator(mapper api.RequestContextMapper, auth authenticator.Request, failed http.Handler, handler http.Handler) (http.Handler, error) {
	return api.NewRequestContextFilter(
		mapper,
		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			user, ok, err := auth.AuthenticateRequest(req)
			if err != nil || !ok {
				if err != nil {
					glog.Errorf("Unable to authenticate the request due to an error: %v", err)
				}
				failed.ServeHTTP(w, req)
				return
			}

			if ctx, ok := mapper.Get(req); ok {
				mapper.Update(req, api.WithUser(ctx, user))
			}

			authenticatedUserCounter.WithLabelValues(compressUsername(user.GetName())).Inc()

			handler.ServeHTTP(w, req)
		}),
	)
}
func TestAppliesTo(t *testing.T) {
	tests := []struct {
		subjects  []rbac.Subject
		ctx       api.Context
		appliesTo bool
		testCase  string
	}{
		{
			subjects: []rbac.Subject{
				{Kind: rbac.UserKind, Name: "foobar"},
			},
			ctx:       api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
			appliesTo: true,
			testCase:  "single subject that matches username",
		},
		{
			subjects: []rbac.Subject{
				{Kind: rbac.UserKind, Name: "barfoo"},
				{Kind: rbac.UserKind, Name: "foobar"},
			},
			ctx:       api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
			appliesTo: true,
			testCase:  "multiple subjects, one that matches username",
		},
		{
			subjects: []rbac.Subject{
				{Kind: rbac.UserKind, Name: "barfoo"},
				{Kind: rbac.UserKind, Name: "foobar"},
			},
			ctx:       api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam"}),
			appliesTo: false,
			testCase:  "multiple subjects, none that match username",
		},
		{
			subjects: []rbac.Subject{
				{Kind: rbac.UserKind, Name: "barfoo"},
				{Kind: rbac.GroupKind, Name: "foobar"},
			},
			ctx:       api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
			appliesTo: true,
			testCase:  "multiple subjects, one that match group",
		},
		{
			subjects: []rbac.Subject{
				{Kind: rbac.UserKind, Name: "barfoo"},
				{Kind: rbac.GroupKind, Name: "foobar"},
			},
			ctx: api.WithNamespace(
				api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
				"namespace1",
			),
			appliesTo: true,
			testCase:  "multiple subjects, one that match group, should ignore namespace",
		},
		{
			subjects: []rbac.Subject{
				{Kind: rbac.UserKind, Name: "barfoo"},
				{Kind: rbac.GroupKind, Name: "foobar"},
				{Kind: rbac.ServiceAccountKind, Name: "kube-system", Namespace: "default"},
			},
			ctx: api.WithNamespace(
				api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}),
				"default",
			),
			appliesTo: true,
			testCase:  "multiple subjects with a service account that matches",
		},
		{
			subjects: []rbac.Subject{
				{Kind: rbac.UserKind, Name: "*"},
			},
			ctx: api.WithNamespace(
				api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
				"default",
			),
			appliesTo: true,
			testCase:  "multiple subjects with a service account that matches",
		},
	}

	for _, tc := range tests {
		got, err := appliesTo(tc.ctx, tc.subjects)
		if err != nil {
			t.Errorf("case %q %v", tc.testCase, err)
			continue
		}
		if got != tc.appliesTo {
			t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got)
		}
	}
}
func TestDefaultRuleResolver(t *testing.T) {
	ruleReadPods := rbac.PolicyRule{
		Verbs:     []string{"GET", "WATCH"},
		APIGroups: []string{"v1"},
		Resources: []string{"pods"},
	}
	ruleReadServices := rbac.PolicyRule{
		Verbs:     []string{"GET", "WATCH"},
		APIGroups: []string{"v1"},
		Resources: []string{"services"},
	}
	ruleWriteNodes := rbac.PolicyRule{
		Verbs:     []string{"PUT", "CREATE", "UPDATE"},
		APIGroups: []string{"v1"},
		Resources: []string{"nodes"},
	}
	ruleAdmin := rbac.PolicyRule{
		Verbs:     []string{"*"},
		APIGroups: []string{"*"},
		Resources: []string{"*"},
	}

	staticRoles1 := staticRoles{
		roles: []rbac.Role{
			{
				ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
				Rules:      []rbac.PolicyRule{ruleReadPods, ruleReadServices},
			},
		},
		clusterRoles: []rbac.ClusterRole{
			{
				ObjectMeta: api.ObjectMeta{Name: "cluster-admin"},
				Rules:      []rbac.PolicyRule{ruleAdmin},
			},
			{
				ObjectMeta: api.ObjectMeta{Name: "write-nodes"},
				Rules:      []rbac.PolicyRule{ruleWriteNodes},
			},
		},
		roleBindings: []rbac.RoleBinding{
			{
				ObjectMeta: api.ObjectMeta{Namespace: "namespace1"},
				Subjects: []rbac.Subject{
					{Kind: rbac.UserKind, Name: "foobar"},
					{Kind: rbac.GroupKind, Name: "group1"},
				},
				RoleRef: api.ObjectReference{Kind: "Role", Namespace: "namespace1", Name: "readthings"},
			},
		},
		clusterRoleBindings: []rbac.ClusterRoleBinding{
			{
				Subjects: []rbac.Subject{
					{Kind: rbac.UserKind, Name: "admin"},
					{Kind: rbac.GroupKind, Name: "admin"},
				},
				RoleRef: api.ObjectReference{Kind: "ClusterRole", Name: "cluster-admin"},
			},
		},
	}

	tests := []struct {
		staticRoles

		// For a given context, what are the rules that apply?
		ctx            api.Context
		effectiveRules []rbac.PolicyRule
	}{
		{
			staticRoles: staticRoles1,
			ctx: api.WithNamespace(
				api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace1",
			),
			effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
		},
		{
			staticRoles: staticRoles1,
			ctx: api.WithNamespace(
				// Same as above but diffrerent namespace. Should return no rules.
				api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace2",
			),
			effectiveRules: []rbac.PolicyRule{},
		},
		{
			staticRoles: staticRoles1,
			// GetEffectivePolicyRules only returns the policies for the namespace, not the master namespace.
			ctx: api.WithNamespace(
				api.WithUser(api.NewContext(), &user.DefaultInfo{
					Name: "foobar", Groups: []string{"admin"},
				}), "namespace1",
			),
			effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
		},
		{
			staticRoles: staticRoles1,
			// Same as above but without a namespace. Only cluster rules should apply.
			ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{
				Name: "foobar", Groups: []string{"admin"},
			}),
			effectiveRules: []rbac.PolicyRule{ruleAdmin},
		},
		{
			staticRoles:    staticRoles1,
			ctx:            api.WithUser(api.NewContext(), &user.DefaultInfo{}),
			effectiveRules: []rbac.PolicyRule{},
		},
	}

	for i, tc := range tests {
		ruleResolver := newMockRuleResolver(&tc.staticRoles)
		rules, err := ruleResolver.GetEffectivePolicyRules(tc.ctx)
		if err != nil {
			t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
			continue
		}

		// Sort for deep equals
		sort.Sort(byHash(rules))
		sort.Sort(byHash(tc.effectiveRules))

		if !reflect.DeepEqual(rules, tc.effectiveRules) {
			ruleDiff := diff.ObjectDiff(rules, tc.effectiveRules)
			t.Errorf("case %d: %s", i, ruleDiff)
		}
	}
}
Beispiel #4
0
func WithImpersonation(handler http.Handler, requestContextMapper api.RequestContextMapper, a authorizer.Authorizer) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		requestedSubject := req.Header.Get("Impersonate-User")
		if len(requestedSubject) == 0 {
			handler.ServeHTTP(w, req)
			return
		}

		ctx, exists := requestContextMapper.Get(req)
		if !exists {
			forbidden(w, req)
			return
		}
		requestor, exists := api.UserFrom(ctx)
		if !exists {
			forbidden(w, req)
			return
		}

		actingAsAttributes := &authorizer.AttributesRecord{
			User:            requestor,
			Verb:            "impersonate",
			APIGroup:        api.GroupName,
			Resource:        "users",
			Name:            requestedSubject,
			ResourceRequest: true,
		}
		if namespace, name, err := serviceaccount.SplitUsername(requestedSubject); err == nil {
			actingAsAttributes.Resource = "serviceaccounts"
			actingAsAttributes.Namespace = namespace
			actingAsAttributes.Name = name
		}

		err := a.Authorize(actingAsAttributes)
		if err != nil {
			forbidden(w, req)
			return
		}

		switch {
		case strings.HasPrefix(requestedSubject, serviceaccount.ServiceAccountUsernamePrefix):
			namespace, name, err := serviceaccount.SplitUsername(requestedSubject)
			if err != nil {
				forbidden(w, req)
				return
			}
			requestContextMapper.Update(req, api.WithUser(ctx, serviceaccount.UserInfo(namespace, name, "")))

		default:
			newUser := &user.DefaultInfo{
				Name: requestedSubject,
			}
			requestContextMapper.Update(req, api.WithUser(ctx, newUser))
		}

		newCtx, _ := requestContextMapper.Get(req)
		oldUser, _ := api.UserFrom(ctx)
		newUser, _ := api.UserFrom(newCtx)
		httplog.LogOf(req, w).Addf("%v is acting as %v", oldUser, newUser)

		handler.ServeHTTP(w, req)
	})
}