@@ -26,15 +26,17 @@ import (
26
26
"time"
27
27
28
28
"github.com/Jeffail/gabs"
29
- "github.com/openshift/cluster-monitoring-operator/test/e2e/framework"
30
29
"github.com/pkg/errors"
31
30
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
32
31
appsv1 "k8s.io/api/apps/v1"
33
32
v1 "k8s.io/api/core/v1"
33
+ rbacv1 "k8s.io/api/rbac/v1"
34
34
apierrors "k8s.io/apimachinery/pkg/api/errors"
35
35
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36
36
"k8s.io/apimachinery/pkg/util/wait"
37
37
"k8s.io/client-go/util/cert"
38
+
39
+ "github.com/openshift/cluster-monitoring-operator/test/e2e/framework"
38
40
)
39
41
40
42
type scenario struct {
@@ -525,16 +527,10 @@ func assertTenancyForMetrics(t *testing.T) {
525
527
526
528
err := framework .Poll (2 * time .Second , 10 * time .Second , func () error {
527
529
_ , err := f .CreateServiceAccount (userWorkloadTestNs , testAccount )
528
- return err
529
- })
530
- if err != nil {
531
- t .Fatal (err )
532
- }
533
-
534
- // Grant enough permissions to the account so it can read metrics.
535
- err = framework .Poll (2 * time .Second , 10 * time .Second , func () error {
536
- _ , err = f .CreateRoleBindingFromClusterRole (userWorkloadTestNs , testAccount , "admin" )
537
- return err
530
+ if ! apierrors .IsAlreadyExists (err ) {
531
+ return err
532
+ }
533
+ return nil
538
534
})
539
535
if err != nil {
540
536
t .Fatal (err )
@@ -576,6 +572,32 @@ func assertTenancyForMetrics(t *testing.T) {
576
572
t .Run (tc .name , func (t * testing.T ) {
577
573
t .Logf ("Running query %q" , tc .query )
578
574
575
+ var cleanupFn func () error
576
+ // Grant just-enough permissions to the account so it can read metrics.
577
+ err = framework .Poll (2 * time .Second , 10 * time .Second , func () error {
578
+ cleanupFn , err = f .CreateRoleBindingFromTypedRole (userWorkloadTestNs , testAccount , & rbacv1.Role {
579
+ ObjectMeta : metav1.ObjectMeta {
580
+ Name : "tenancy-test-metrics" ,
581
+ },
582
+ Rules : []rbacv1.PolicyRule {
583
+ {
584
+ APIGroups : []string {"metrics.k8s.io" },
585
+ Resources : []string {"pods" },
586
+ Verbs : []string {"get" },
587
+ },
588
+ },
589
+ })
590
+ return err
591
+ })
592
+ if err != nil {
593
+ t .Fatal (err )
594
+ }
595
+ defer func () {
596
+ if err := cleanupFn (); err != nil {
597
+ t .Fatal (err )
598
+ }
599
+ }()
600
+
579
601
err = framework .Poll (5 * time .Second , time .Minute , func () error {
580
602
// The tenancy port (9092) is only exposed in-cluster so we need to use
581
603
// port forwarding to access kube-rbac-proxy.
@@ -681,6 +703,122 @@ func assertTenancyForMetrics(t *testing.T) {
681
703
if err != nil {
682
704
t .Fatalf ("the account has access to the rules endpoint of Thanos querier: %v" , err )
683
705
}
706
+
707
+ for _ , tc := range []struct {
708
+ role rbacv1.Role
709
+ expectNotOKOnQuery bool
710
+ desc string
711
+ method string
712
+ }{
713
+
714
+ {
715
+ role : rbacv1.Role {
716
+ ObjectMeta : metav1.ObjectMeta {
717
+ Name : "tenancy-test-metrics" ,
718
+ },
719
+ Rules : []rbacv1.PolicyRule {
720
+ {
721
+ APIGroups : []string {"metrics.k8s.io" },
722
+ Resources : []string {"pods" },
723
+ Verbs : []string {"get" },
724
+ },
725
+ },
726
+ },
727
+ method : http .MethodPost ,
728
+ expectNotOKOnQuery : true ,
729
+ desc : "should disallow POST queries to the endpoint for SA with no create permission" ,
730
+ },
731
+ {
732
+ role : rbacv1.Role {
733
+ ObjectMeta : metav1.ObjectMeta {
734
+ Name : "tenancy-test-metrics" ,
735
+ },
736
+ Rules : []rbacv1.PolicyRule {
737
+ {
738
+ APIGroups : []string {"metrics.k8s.io" },
739
+ Resources : []string {"pods" },
740
+ Verbs : []string {"get" },
741
+ },
742
+ },
743
+ },
744
+ method : http .MethodGet ,
745
+ desc : "should allow GET queries to the endpoint for SA with get permission" ,
746
+ },
747
+ {
748
+ role : rbacv1.Role {
749
+ ObjectMeta : metav1.ObjectMeta {
750
+ Name : "tenancy-test-metrics" ,
751
+ },
752
+ Rules : []rbacv1.PolicyRule {
753
+ {
754
+ APIGroups : []string {"metrics.k8s.io" },
755
+ Resources : []string {"pods" },
756
+ Verbs : []string {"get" , "create" },
757
+ },
758
+ },
759
+ },
760
+ method : http .MethodPost ,
761
+ desc : "should allow POST queries to the endpoint for SA with get and create permission" ,
762
+ },
763
+ } {
764
+ t .Run (tc .desc , func (t * testing.T ) {
765
+ var cleanupFn framework.CleanUpFunc
766
+
767
+ // Create a role binding for the test SA.
768
+ err = framework .Poll (time .Second , time .Minute , func () error {
769
+ cleanupFn , err = f .CreateRoleBindingFromTypedRole (userWorkloadTestNs , testAccount , & tc .role )
770
+ return err
771
+ })
772
+ if err != nil {
773
+ t .Fatal (err )
774
+ }
775
+
776
+ // Remove associated artifacts.
777
+ defer func () {
778
+ if err := cleanupFn (); err != nil {
779
+ t .Fatal (err )
780
+ }
781
+ }()
782
+
783
+ // Forward the tenancy port.
784
+ host , cleanUp , err := f .ForwardPort (t , f .Ns , "thanos-querier" , 9092 )
785
+ if err != nil {
786
+ t .Fatal (err )
787
+ }
788
+ defer cleanUp ()
789
+
790
+ // Create a Prometheus client with the test SA token.
791
+ client := framework .NewPrometheusClient (
792
+ host ,
793
+ token ,
794
+ & framework.QueryParameterInjector {
795
+ Name : "namespace" ,
796
+ Value : userWorkloadTestNs ,
797
+ },
798
+ )
799
+
800
+ resp , err := client .Do (tc .method , "/api/v1/query?namespace=" + userWorkloadTestNs + "&query=up" , nil )
801
+ if err != nil {
802
+ t .Fatal (err )
803
+ }
804
+ defer resp .Body .Close ()
805
+ // Body: {"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"up",...},"value":[1695582946.784,"1"]}]}}
806
+ respBodyBytes , err := io .ReadAll (resp .Body )
807
+ if err != nil {
808
+ t .Fatal (err )
809
+ }
810
+
811
+ if tc .expectNotOKOnQuery {
812
+ if resp .StatusCode == http .StatusOK {
813
+ t .Fatal ("expected request to be rejected, but succeeded" )
814
+ }
815
+ } else {
816
+ if resp .StatusCode != http .StatusOK {
817
+ t .Fatalf ("expected request to be accepted, but got status code %d (%s)" , resp .StatusCode , respBodyBytes )
818
+ }
819
+ }
820
+ })
821
+ }
684
822
}
685
823
686
824
// assertTenancyForRules ensures that a tenant can access rules from her namespace (and only from this one).
0 commit comments