Skip to content

Commit 794f9de

Browse files
Merge pull request openshift#43 from deads2k/authz
add hardcoded authorizer to approve /metrics for metrics scraper
2 parents 6ea3294 + 3fea9a7 commit 794f9de

File tree

5 files changed

+242
-3
lines changed

5 files changed

+242
-3
lines changed

main.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"golang.org/x/net/http2"
3939
"golang.org/x/net/http2/h2c"
4040
"k8s.io/apiserver/pkg/authentication/authenticator"
41+
"k8s.io/apiserver/pkg/authorization/union"
4142
"k8s.io/client-go/kubernetes"
4243
"k8s.io/client-go/rest"
4344
"k8s.io/client-go/tools/clientcmd"
@@ -47,6 +48,7 @@ import (
4748

4849
"github.com/brancz/kube-rbac-proxy/pkg/authn"
4950
"github.com/brancz/kube-rbac-proxy/pkg/authz"
51+
"github.com/brancz/kube-rbac-proxy/pkg/hardcodedauthorizer"
5052
"github.com/brancz/kube-rbac-proxy/pkg/proxy"
5153
rbac_proxy_tls "github.com/brancz/kube-rbac-proxy/pkg/tls"
5254
)
@@ -199,10 +201,15 @@ func main() {
199201

200202
sarClient := kubeClient.AuthorizationV1().SubjectAccessReviews()
201203
authorizer, err := authz.NewAuthorizer(sarClient)
202-
203204
if err != nil {
204205
klog.Fatalf("Failed to create authorizer: %v", err)
205206
}
207+
authorizer = union.New(
208+
// prefix the authorizer with the permissions for metrics scraping which are well known.
209+
// openshift RBAC policy will always allow this user to read metrics.
210+
hardcodedauthorizer.NewHardCodedMetricsAuthorizer(),
211+
authorizer,
212+
)
206213

207214
auth, err := proxy.New(kubeClient, cfg.auth, authorizer, authenticator)
208215

@@ -388,14 +395,14 @@ func initKubeConfig(kcLocation string) *rest.Config {
388395
if kcLocation != "" {
389396
kubeConfig, err := clientcmd.BuildConfigFromFlags("", kcLocation)
390397
if err != nil {
391-
klog.Fatalf("unable to build rest config based on provided path to kubeconfig file: %v",err)
398+
klog.Fatalf("unable to build rest config based on provided path to kubeconfig file: %v", err)
392399
}
393400
return kubeConfig
394401
}
395402

396403
kubeConfig, err := rest.InClusterConfig()
397404
if err != nil {
398-
klog.Fatalf("cannot find Service Account in pod to build in-cluster rest config: %v",err)
405+
klog.Fatalf("cannot find Service Account in pod to build in-cluster rest config: %v", err)
399406
}
400407

401408
return kubeConfig

pkg/hardcodedauthorizer/metrics.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2021 Frederic Branczyk All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// this is copied from library-go to avoid a hard dependency
18+
package hardcodedauthorizer
19+
20+
import (
21+
"context"
22+
23+
"k8s.io/apiserver/pkg/authorization/authorizer"
24+
)
25+
26+
type metricsAuthorizer struct{}
27+
28+
// GetUser() user.Info - checked
29+
// GetVerb() string - checked
30+
// IsReadOnly() bool - na
31+
// GetNamespace() string - na
32+
// GetResource() string - na
33+
// GetSubresource() string - na
34+
// GetName() string - na
35+
// GetAPIGroup() string - na
36+
// GetAPIVersion() string - na
37+
// IsResourceRequest() bool - checked
38+
// GetPath() string - checked
39+
func (metricsAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
40+
if a.GetUser() == nil {
41+
return authorizer.DecisionNoOpinion, "", nil
42+
}
43+
if a.GetUser().GetName() != "system:serviceaccount:openshift-monitoring:prometheus-k8s" {
44+
return authorizer.DecisionNoOpinion, "", nil
45+
}
46+
if !a.IsResourceRequest() &&
47+
a.GetVerb() == "get" &&
48+
a.GetPath() == "/metrics" {
49+
return authorizer.DecisionAllow, "requesting metrics is allowed", nil
50+
}
51+
52+
return authorizer.DecisionNoOpinion, "", nil
53+
}
54+
55+
// NewHardCodedMetricsAuthorizer returns a hardcoded authorizer for checking metrics.
56+
func NewHardCodedMetricsAuthorizer() *metricsAuthorizer {
57+
return new(metricsAuthorizer)
58+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
Copyright 2021 Frederic Branczyk All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package hardcodedauthorizer
18+
19+
import (
20+
"context"
21+
"testing"
22+
23+
"k8s.io/apiserver/pkg/authentication/user"
24+
"k8s.io/apiserver/pkg/authorization/authorizer"
25+
)
26+
27+
func TestAuthorizer(t *testing.T) {
28+
tests := []struct {
29+
name string
30+
authorizer authorizer.Authorizer
31+
32+
shouldPass []authorizer.Attributes
33+
shouldNoOpinion []authorizer.Attributes
34+
}{
35+
{
36+
name: "metrics",
37+
authorizer: NewHardCodedMetricsAuthorizer(),
38+
shouldPass: []authorizer.Attributes{
39+
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "system:serviceaccount:openshift-monitoring:prometheus-k8s"}, Verb: "get", Path: "/metrics"},
40+
},
41+
shouldNoOpinion: []authorizer.Attributes{
42+
// wrong user
43+
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "other"}, Verb: "get", Path: "/metrics"},
44+
// wrong verb
45+
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "system:serviceaccount:openshift-monitoring:prometheus-k8s"}, Verb: "update", Path: "/metrics"},
46+
47+
// wrong path
48+
authorizer.AttributesRecord{User: &user.DefaultInfo{Name: "system:serviceaccount:openshift-monitoring:prometheus-k8s"}, Verb: "get", Path: "/api"},
49+
},
50+
},
51+
}
52+
for _, tt := range tests {
53+
t.Run(tt.name, func(t *testing.T) {
54+
for _, attr := range tt.shouldPass {
55+
if decision, _, _ := tt.authorizer.Authorize(context.Background(), attr); decision != authorizer.DecisionAllow {
56+
t.Errorf("incorrectly restricted %v", attr)
57+
}
58+
}
59+
60+
for _, attr := range tt.shouldNoOpinion {
61+
if decision, _, _ := tt.authorizer.Authorize(context.Background(), attr); decision != authorizer.DecisionNoOpinion {
62+
t.Errorf("incorrectly opinionated %v", attr)
63+
}
64+
}
65+
})
66+
}
67+
}

vendor/k8s.io/apiserver/pkg/authorization/union/union.go

Lines changed: 106 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/modules.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ k8s.io/apiserver/pkg/authentication/token/tokenfile
324324
k8s.io/apiserver/pkg/authentication/user
325325
k8s.io/apiserver/pkg/authorization/authorizer
326326
k8s.io/apiserver/pkg/authorization/authorizerfactory
327+
k8s.io/apiserver/pkg/authorization/union
327328
k8s.io/apiserver/pkg/endpoints/request
328329
k8s.io/apiserver/pkg/server/dynamiccertificates
329330
k8s.io/apiserver/pkg/server/egressselector

0 commit comments

Comments
 (0)