| ... | ... |
@@ -51,15 +51,16 @@ const ( |
| 51 | 51 |
|
| 52 | 52 |
// Roles |
| 53 | 53 |
const ( |
| 54 |
- ClusterAdminRoleName = "cluster-admin" |
|
| 55 |
- SudoerRoleName = "sudoer" |
|
| 56 |
- ClusterReaderRoleName = "cluster-reader" |
|
| 57 |
- AdminRoleName = "admin" |
|
| 58 |
- EditRoleName = "edit" |
|
| 59 |
- ViewRoleName = "view" |
|
| 60 |
- SelfProvisionerRoleName = "self-provisioner" |
|
| 61 |
- BasicUserRoleName = "basic-user" |
|
| 62 |
- StatusCheckerRoleName = "cluster-status" |
|
| 54 |
+ ClusterAdminRoleName = "cluster-admin" |
|
| 55 |
+ SudoerRoleName = "sudoer" |
|
| 56 |
+ ClusterReaderRoleName = "cluster-reader" |
|
| 57 |
+ AdminRoleName = "admin" |
|
| 58 |
+ EditRoleName = "edit" |
|
| 59 |
+ ViewRoleName = "view" |
|
| 60 |
+ SelfProvisionerRoleName = "self-provisioner" |
|
| 61 |
+ BasicUserRoleName = "basic-user" |
|
| 62 |
+ StatusCheckerRoleName = "cluster-status" |
|
| 63 |
+ SelfAccessReviewerRoleName = "self-access-reviewer" |
|
| 63 | 64 |
|
| 64 | 65 |
RegistryAdminRoleName = "registry-admin" |
| 65 | 66 |
RegistryViewerRoleName = "registry-viewer" |
| ... | ... |
@@ -97,29 +98,30 @@ const ( |
| 97 | 97 |
|
| 98 | 98 |
// RoleBindings |
| 99 | 99 |
const ( |
| 100 |
- SelfProvisionerRoleBindingName = SelfProvisionerRoleName + "s" |
|
| 101 |
- DeployerRoleBindingName = DeployerRoleName + "s" |
|
| 102 |
- ClusterAdminRoleBindingName = ClusterAdminRoleName + "s" |
|
| 103 |
- ClusterReaderRoleBindingName = ClusterReaderRoleName + "s" |
|
| 104 |
- BasicUserRoleBindingName = BasicUserRoleName + "s" |
|
| 105 |
- OAuthTokenDeleterRoleBindingName = OAuthTokenDeleterRoleName + "s" |
|
| 106 |
- StatusCheckerRoleBindingName = StatusCheckerRoleName + "-binding" |
|
| 107 |
- ImagePullerRoleBindingName = ImagePullerRoleName + "s" |
|
| 108 |
- ImageBuilderRoleBindingName = ImageBuilderRoleName + "s" |
|
| 109 |
- RouterRoleBindingName = RouterRoleName + "s" |
|
| 110 |
- RegistryRoleBindingName = RegistryRoleName + "s" |
|
| 111 |
- MasterRoleBindingName = MasterRoleName + "s" |
|
| 112 |
- NodeRoleBindingName = NodeRoleName + "s" |
|
| 113 |
- NodeProxierRoleBindingName = NodeProxierRoleName + "s" |
|
| 114 |
- NodeAdminRoleBindingName = NodeAdminRoleName + "s" |
|
| 115 |
- NodeReaderRoleBindingName = NodeReaderRoleName + "s" |
|
| 116 |
- SDNReaderRoleBindingName = SDNReaderRoleName + "s" |
|
| 117 |
- SDNManagerRoleBindingName = SDNManagerRoleName + "s" |
|
| 118 |
- WebHooksRoleBindingName = WebHooksRoleName + "s" |
|
| 119 |
- DiscoveryRoleBindingName = DiscoveryRoleName + "-binding" |
|
| 120 |
- RegistryAdminRoleBindingName = RegistryAdminRoleName + "s" |
|
| 121 |
- RegistryViewerRoleBindingName = RegistryViewerRoleName + "s" |
|
| 122 |
- RegistryEditorRoleBindingName = RegistryEditorRoleName + "s" |
|
| 100 |
+ SelfAccessReviewerRoleBindingName = SelfAccessReviewerRoleName + "s" |
|
| 101 |
+ SelfProvisionerRoleBindingName = SelfProvisionerRoleName + "s" |
|
| 102 |
+ DeployerRoleBindingName = DeployerRoleName + "s" |
|
| 103 |
+ ClusterAdminRoleBindingName = ClusterAdminRoleName + "s" |
|
| 104 |
+ ClusterReaderRoleBindingName = ClusterReaderRoleName + "s" |
|
| 105 |
+ BasicUserRoleBindingName = BasicUserRoleName + "s" |
|
| 106 |
+ OAuthTokenDeleterRoleBindingName = OAuthTokenDeleterRoleName + "s" |
|
| 107 |
+ StatusCheckerRoleBindingName = StatusCheckerRoleName + "-binding" |
|
| 108 |
+ ImagePullerRoleBindingName = ImagePullerRoleName + "s" |
|
| 109 |
+ ImageBuilderRoleBindingName = ImageBuilderRoleName + "s" |
|
| 110 |
+ RouterRoleBindingName = RouterRoleName + "s" |
|
| 111 |
+ RegistryRoleBindingName = RegistryRoleName + "s" |
|
| 112 |
+ MasterRoleBindingName = MasterRoleName + "s" |
|
| 113 |
+ NodeRoleBindingName = NodeRoleName + "s" |
|
| 114 |
+ NodeProxierRoleBindingName = NodeProxierRoleName + "s" |
|
| 115 |
+ NodeAdminRoleBindingName = NodeAdminRoleName + "s" |
|
| 116 |
+ NodeReaderRoleBindingName = NodeReaderRoleName + "s" |
|
| 117 |
+ SDNReaderRoleBindingName = SDNReaderRoleName + "s" |
|
| 118 |
+ SDNManagerRoleBindingName = SDNManagerRoleName + "s" |
|
| 119 |
+ WebHooksRoleBindingName = WebHooksRoleName + "s" |
|
| 120 |
+ DiscoveryRoleBindingName = DiscoveryRoleName + "-binding" |
|
| 121 |
+ RegistryAdminRoleBindingName = RegistryAdminRoleName + "s" |
|
| 122 |
+ RegistryViewerRoleBindingName = RegistryViewerRoleName + "s" |
|
| 123 |
+ RegistryEditorRoleBindingName = RegistryEditorRoleName + "s" |
|
| 123 | 124 |
|
| 124 | 125 |
BuildStrategyDockerRoleBindingName = BuildStrategyDockerRoleName + "-binding" |
| 125 | 126 |
BuildStrategyCustomRoleBindingName = BuildStrategyCustomRoleName + "-binding" |
| ... | ... |
@@ -361,6 +361,15 @@ func GetBootstrapClusterRoles() []authorizationapi.ClusterRole {
|
| 361 | 361 |
}, |
| 362 | 362 |
{
|
| 363 | 363 |
ObjectMeta: kapi.ObjectMeta{
|
| 364 |
+ Name: SelfAccessReviewerRoleName, |
|
| 365 |
+ }, |
|
| 366 |
+ Rules: []authorizationapi.PolicyRule{
|
|
| 367 |
+ authorizationapi.NewRule("create").Groups(authzGroup).Resources("selfsubjectrulesreviews").RuleOrDie(),
|
|
| 368 |
+ {Verbs: sets.NewString("create"), APIGroups: []string{authzGroup}, Resources: sets.NewString("subjectaccessreviews", "localsubjectaccessreviews"), AttributeRestrictions: &authorizationapi.IsPersonalSubjectAccessReview{}},
|
|
| 369 |
+ }, |
|
| 370 |
+ }, |
|
| 371 |
+ {
|
|
| 372 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 364 | 373 |
Name: SelfProvisionerRoleName, |
| 365 | 374 |
}, |
| 366 | 375 |
Rules: []authorizationapi.PolicyRule{
|
| ... | ... |
@@ -774,6 +783,18 @@ func GetBootstrapClusterRoleBindings() []authorizationapi.ClusterRoleBinding {
|
| 774 | 774 |
}, |
| 775 | 775 |
{
|
| 776 | 776 |
ObjectMeta: kapi.ObjectMeta{
|
| 777 |
+ Name: SelfAccessReviewerRoleBindingName, |
|
| 778 |
+ }, |
|
| 779 |
+ RoleRef: kapi.ObjectReference{
|
|
| 780 |
+ Name: SelfAccessReviewerRoleName, |
|
| 781 |
+ }, |
|
| 782 |
+ Subjects: []kapi.ObjectReference{
|
|
| 783 |
+ {Kind: authorizationapi.SystemGroupKind, Name: AuthenticatedGroup},
|
|
| 784 |
+ {Kind: authorizationapi.SystemGroupKind, Name: UnauthenticatedGroup},
|
|
| 785 |
+ }, |
|
| 786 |
+ }, |
|
| 787 |
+ {
|
|
| 788 |
+ ObjectMeta: kapi.ObjectMeta{
|
|
| 777 | 789 |
Name: SelfProvisionerRoleBindingName, |
| 778 | 790 |
}, |
| 779 | 791 |
RoleRef: kapi.ObjectReference{
|
| ... | ... |
@@ -21,6 +21,7 @@ import ( |
| 21 | 21 |
"github.com/openshift/origin/pkg/client" |
| 22 | 22 |
policy "github.com/openshift/origin/pkg/cmd/admin/policy" |
| 23 | 23 |
"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy" |
| 24 |
+ "github.com/openshift/origin/pkg/cmd/util/clientcmd" |
|
| 24 | 25 |
deployapi "github.com/openshift/origin/pkg/deploy/api" |
| 25 | 26 |
imageapi "github.com/openshift/origin/pkg/image/api" |
| 26 | 27 |
oauthapi "github.com/openshift/origin/pkg/oauth/api" |
| ... | ... |
@@ -732,6 +733,22 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 732 | 732 |
t.Fatalf("error requesting token: %v", err)
|
| 733 | 733 |
} |
| 734 | 734 |
|
| 735 |
+ anonymousConfig := clientcmd.AnonymousClientConfig(clusterAdminClientConfig) |
|
| 736 |
+ anonymousClient, err := client.New(&anonymousConfig) |
|
| 737 |
+ if err != nil {
|
|
| 738 |
+ t.Fatalf("error getting anonymous client: %v", err)
|
|
| 739 |
+ } |
|
| 740 |
+ |
|
| 741 |
+ addAnonymous := &policy.RoleModificationOptions{
|
|
| 742 |
+ RoleNamespace: "", |
|
| 743 |
+ RoleName: bootstrappolicy.EditRoleName, |
|
| 744 |
+ RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", clusterAdminClient),
|
|
| 745 |
+ Users: []string{"system:anonymous"},
|
|
| 746 |
+ } |
|
| 747 |
+ if err := addAnonymous.AddRole(); err != nil {
|
|
| 748 |
+ t.Errorf("unexpected error: %v", err)
|
|
| 749 |
+ } |
|
| 750 |
+ |
|
| 735 | 751 |
addDanny := &policy.RoleModificationOptions{
|
| 736 | 752 |
RoleNamespace: "", |
| 737 | 753 |
RoleName: bootstrappolicy.ViewRoleName, |
| ... | ... |
@@ -774,6 +791,12 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 774 | 774 |
clusterReview: askCanDannyGetProject, |
| 775 | 775 |
err: `User "danny" cannot create subjectaccessreviews at the cluster scope`, |
| 776 | 776 |
}.run(t) |
| 777 |
+ subjectAccessReviewTest{
|
|
| 778 |
+ description: "as anonymous, can I make cluster subject access reviews", |
|
| 779 |
+ clusterInterface: anonymousClient.SubjectAccessReviews(), |
|
| 780 |
+ clusterReview: askCanDannyGetProject, |
|
| 781 |
+ err: `User "system:anonymous" cannot create subjectaccessreviews at the cluster scope`, |
|
| 782 |
+ }.run(t) |
|
| 777 | 783 |
|
| 778 | 784 |
addValerie := &policy.RoleModificationOptions{
|
| 779 | 785 |
RoleNamespace: "", |
| ... | ... |
@@ -834,12 +857,32 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 834 | 834 |
Namespace: "mallet-project", |
| 835 | 835 |
}, |
| 836 | 836 |
}.run(t) |
| 837 |
+ // ensure unprivileged users cannot check other users' access |
|
| 837 | 838 |
subjectAccessReviewTest{
|
| 838 | 839 |
description: "harold denied ability to run subject access review in project mallet-project", |
| 839 | 840 |
localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"),
|
| 840 | 841 |
localReview: askCanEdgarDeletePods, |
| 841 | 842 |
err: `User "harold" cannot create localsubjectaccessreviews in project "mallet-project"`, |
| 842 | 843 |
}.run(t) |
| 844 |
+ subjectAccessReviewTest{
|
|
| 845 |
+ description: "system:anonymous denied ability to run subject access review in project mallet-project", |
|
| 846 |
+ localInterface: anonymousClient.LocalSubjectAccessReviews("mallet-project"),
|
|
| 847 |
+ localReview: askCanEdgarDeletePods, |
|
| 848 |
+ err: `User "system:anonymous" cannot create localsubjectaccessreviews in project "mallet-project"`, |
|
| 849 |
+ }.run(t) |
|
| 850 |
+ // ensure message does not leak whether the namespace exists or not |
|
| 851 |
+ subjectAccessReviewTest{
|
|
| 852 |
+ description: "harold denied ability to run subject access review in project nonexistent-project", |
|
| 853 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("nonexistent-project"),
|
|
| 854 |
+ localReview: askCanEdgarDeletePods, |
|
| 855 |
+ err: `User "harold" cannot create localsubjectaccessreviews in project "nonexistent-project"`, |
|
| 856 |
+ }.run(t) |
|
| 857 |
+ subjectAccessReviewTest{
|
|
| 858 |
+ description: "system:anonymous denied ability to run subject access review in project nonexistent-project", |
|
| 859 |
+ localInterface: anonymousClient.LocalSubjectAccessReviews("nonexistent-project"),
|
|
| 860 |
+ localReview: askCanEdgarDeletePods, |
|
| 861 |
+ err: `User "system:anonymous" cannot create localsubjectaccessreviews in project "nonexistent-project"`, |
|
| 862 |
+ }.run(t) |
|
| 843 | 863 |
|
| 844 | 864 |
askCanHaroldUpdateProject := &authorizationapi.LocalSubjectAccessReview{
|
| 845 | 865 |
User: "harold", |
| ... | ... |
@@ -890,6 +933,61 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
|
| 890 | 890 |
Namespace: "hammer-project", |
| 891 | 891 |
}, |
| 892 | 892 |
}.run(t) |
| 893 |
+ subjectAccessReviewTest{
|
|
| 894 |
+ description: "system:anonymous told he can create pods in project hammer-project", |
|
| 895 |
+ localInterface: anonymousClient.LocalSubjectAccessReviews("hammer-project"),
|
|
| 896 |
+ localReview: askCanICreatePods, |
|
| 897 |
+ response: authorizationapi.SubjectAccessReviewResponse{
|
|
| 898 |
+ Allowed: true, |
|
| 899 |
+ Reason: "allowed by rule in hammer-project", |
|
| 900 |
+ Namespace: "hammer-project", |
|
| 901 |
+ }, |
|
| 902 |
+ }.run(t) |
|
| 903 |
+ |
|
| 904 |
+ // test checking self permissions when denied |
|
| 905 |
+ subjectAccessReviewTest{
|
|
| 906 |
+ description: "harold told he cannot create pods in project mallet-project", |
|
| 907 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"),
|
|
| 908 |
+ localReview: askCanICreatePods, |
|
| 909 |
+ response: authorizationapi.SubjectAccessReviewResponse{
|
|
| 910 |
+ Allowed: false, |
|
| 911 |
+ Reason: `User "harold" cannot create pods in project "mallet-project"`, |
|
| 912 |
+ Namespace: "mallet-project", |
|
| 913 |
+ }, |
|
| 914 |
+ }.run(t) |
|
| 915 |
+ subjectAccessReviewTest{
|
|
| 916 |
+ description: "system:anonymous told he cannot create pods in project mallet-project", |
|
| 917 |
+ localInterface: anonymousClient.LocalSubjectAccessReviews("mallet-project"),
|
|
| 918 |
+ localReview: askCanICreatePods, |
|
| 919 |
+ response: authorizationapi.SubjectAccessReviewResponse{
|
|
| 920 |
+ Allowed: false, |
|
| 921 |
+ Reason: `User "system:anonymous" cannot create pods in project "mallet-project"`, |
|
| 922 |
+ Namespace: "mallet-project", |
|
| 923 |
+ }, |
|
| 924 |
+ }.run(t) |
|
| 925 |
+ |
|
| 926 |
+ // test checking self-permissions doesn't leak whether namespace exists or not |
|
| 927 |
+ subjectAccessReviewTest{
|
|
| 928 |
+ description: "harold told he cannot create pods in project nonexistent-project", |
|
| 929 |
+ localInterface: haroldClient.LocalSubjectAccessReviews("nonexistent-project"),
|
|
| 930 |
+ localReview: askCanICreatePods, |
|
| 931 |
+ response: authorizationapi.SubjectAccessReviewResponse{
|
|
| 932 |
+ Allowed: false, |
|
| 933 |
+ Reason: `User "harold" cannot create pods in project "nonexistent-project"`, |
|
| 934 |
+ Namespace: "nonexistent-project", |
|
| 935 |
+ }, |
|
| 936 |
+ }.run(t) |
|
| 937 |
+ subjectAccessReviewTest{
|
|
| 938 |
+ description: "system:anonymous told he cannot create pods in project nonexistent-project", |
|
| 939 |
+ localInterface: anonymousClient.LocalSubjectAccessReviews("nonexistent-project"),
|
|
| 940 |
+ localReview: askCanICreatePods, |
|
| 941 |
+ response: authorizationapi.SubjectAccessReviewResponse{
|
|
| 942 |
+ Allowed: false, |
|
| 943 |
+ Reason: `User "system:anonymous" cannot create pods in project "nonexistent-project"`, |
|
| 944 |
+ Namespace: "nonexistent-project", |
|
| 945 |
+ }, |
|
| 946 |
+ }.run(t) |
|
| 947 |
+ |
|
| 893 | 948 |
askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{
|
| 894 | 949 |
Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "policybindings"},
|
| 895 | 950 |
} |
| ... | ... |
@@ -73,6 +73,22 @@ items: |
| 73 | 73 |
userNames: null |
| 74 | 74 |
- apiVersion: v1 |
| 75 | 75 |
groupNames: |
| 76 |
+ - system:authenticated |
|
| 77 |
+ - system:unauthenticated |
|
| 78 |
+ kind: ClusterRoleBinding |
|
| 79 |
+ metadata: |
|
| 80 |
+ creationTimestamp: null |
|
| 81 |
+ name: self-access-reviewers |
|
| 82 |
+ roleRef: |
|
| 83 |
+ name: self-access-reviewer |
|
| 84 |
+ subjects: |
|
| 85 |
+ - kind: SystemGroup |
|
| 86 |
+ name: system:authenticated |
|
| 87 |
+ - kind: SystemGroup |
|
| 88 |
+ name: system:unauthenticated |
|
| 89 |
+ userNames: null |
|
| 90 |
+- apiVersion: v1 |
|
| 91 |
+ groupNames: |
|
| 76 | 92 |
- system:authenticated:oauth |
| 77 | 93 |
kind: ClusterRoleBinding |
| 78 | 94 |
metadata: |
| ... | ... |
@@ -1301,6 +1301,29 @@ items: |
| 1301 | 1301 |
kind: ClusterRole |
| 1302 | 1302 |
metadata: |
| 1303 | 1303 |
creationTimestamp: null |
| 1304 |
+ name: self-access-reviewer |
|
| 1305 |
+ rules: |
|
| 1306 |
+ - apiGroups: |
|
| 1307 |
+ - "" |
|
| 1308 |
+ attributeRestrictions: null |
|
| 1309 |
+ resources: |
|
| 1310 |
+ - selfsubjectrulesreviews |
|
| 1311 |
+ verbs: |
|
| 1312 |
+ - create |
|
| 1313 |
+ - apiGroups: |
|
| 1314 |
+ - "" |
|
| 1315 |
+ attributeRestrictions: |
|
| 1316 |
+ apiVersion: v1 |
|
| 1317 |
+ kind: IsPersonalSubjectAccessReview |
|
| 1318 |
+ resources: |
|
| 1319 |
+ - localsubjectaccessreviews |
|
| 1320 |
+ - subjectaccessreviews |
|
| 1321 |
+ verbs: |
|
| 1322 |
+ - create |
|
| 1323 |
+- apiVersion: v1 |
|
| 1324 |
+ kind: ClusterRole |
|
| 1325 |
+ metadata: |
|
| 1326 |
+ creationTimestamp: null |
|
| 1304 | 1327 |
name: self-provisioner |
| 1305 | 1328 |
rules: |
| 1306 | 1329 |
- apiGroups: |