Browse code

Add cluster role bindings diagnostic

Jordan Liggitt authored on 2015/10/09 03:14:26
Showing 3 changed files
... ...
@@ -170,7 +170,7 @@ func (o *ReconcileClusterRoleBindingsOptions) ChangedClusterRoleBindings() ([]*a
170 170
 		actualClusterRoleBinding, err := o.RoleBindingClient.Get(expectedClusterRoleBinding.Name)
171 171
 		if kapierrors.IsNotFound(err) {
172 172
 			// Remove excluded subjects from the new role binding
173
-			expectedClusterRoleBinding.Subjects, _ = diff(expectedClusterRoleBinding.Subjects, o.ExcludeSubjects)
173
+			expectedClusterRoleBinding.Subjects, _ = Diff(expectedClusterRoleBinding.Subjects, o.ExcludeSubjects)
174 174
 			changedRoleBindings = append(changedRoleBindings, expectedClusterRoleBinding)
175 175
 			continue
176 176
 		}
... ...
@@ -249,11 +249,11 @@ func computeUpdatedBinding(expected authorizationapi.ClusterRoleBinding, actual
249 249
 	}
250 250
 
251 251
 	// compute the list of subjects we should not add roles for (existing subjects in the exclude list should be preserved)
252
-	doNotAddSubjects, _ := diff(excludeSubjects, actual.Subjects)
252
+	doNotAddSubjects, _ := Diff(excludeSubjects, actual.Subjects)
253 253
 	// remove any excluded subjects that do not exist from our expected subject list (so we don't add them)
254
-	expectedSubjects, _ := diff(expected.Subjects, doNotAddSubjects)
254
+	expectedSubjects, _ := Diff(expected.Subjects, doNotAddSubjects)
255 255
 
256
-	missingSubjects, extraSubjects := diff(expectedSubjects, actual.Subjects)
256
+	missingSubjects, extraSubjects := Diff(expectedSubjects, actual.Subjects)
257 257
 	// Always add missing expected subjects
258 258
 	if len(missingSubjects) > 0 {
259 259
 		needsUpdating = true
... ...
@@ -284,11 +284,11 @@ func contains(list []kapi.ObjectReference, item kapi.ObjectReference) bool {
284 284
 	return false
285 285
 }
286 286
 
287
-// diff returns lists containing the items unique to each provided list:
287
+// Diff returns lists containing the items unique to each provided list:
288 288
 //   list1Only = list1 - list2
289 289
 //   list2Only = list2 - list1
290 290
 // if both returned lists are empty, the provided lists are equal
291
-func diff(list1 []kapi.ObjectReference, list2 []kapi.ObjectReference) (list1Only []kapi.ObjectReference, list2Only []kapi.ObjectReference) {
291
+func Diff(list1 []kapi.ObjectReference, list2 []kapi.ObjectReference) (list1Only []kapi.ObjectReference, list2Only []kapi.ObjectReference) {
292 292
 	for _, list1Item := range list1 {
293 293
 		if !contains(list2, list1Item) {
294 294
 			if !contains(list1Only, list1Item) {
... ...
@@ -20,7 +20,7 @@ import (
20 20
 var (
21 21
 	// availableClusterDiagnostics contains the names of cluster diagnostics that can be executed
22 22
 	// during a single run of diagnostics. Add more diagnostics to the list as they are defined.
23
-	availableClusterDiagnostics = sets.NewString(clustdiags.NodeDefinitionsName, clustdiags.ClusterRegistryName, clustdiags.ClusterRouterName, clustdiags.ClusterRolesName)
23
+	availableClusterDiagnostics = sets.NewString(clustdiags.NodeDefinitionsName, clustdiags.ClusterRegistryName, clustdiags.ClusterRouterName, clustdiags.ClusterRolesName, clustdiags.ClusterRoleBindingsName)
24 24
 )
25 25
 
26 26
 // buildClusterDiagnostics builds cluster Diagnostic objects if a cluster-admin client can be extracted from the rawConfig passed in.
... ...
@@ -53,6 +53,8 @@ func (o DiagnosticsOptions) buildClusterDiagnostics(rawConfig *clientcmdapi.Conf
53 53
 			diagnostics = append(diagnostics, &clustdiags.ClusterRouter{KubeClient: kclusterClient, OsClient: clusterClient})
54 54
 		case clustdiags.ClusterRolesName:
55 55
 			diagnostics = append(diagnostics, &clustdiags.ClusterRoles{ClusterRolesClient: clusterClient, SARClient: clusterClient})
56
+		case clustdiags.ClusterRoleBindingsName:
57
+			diagnostics = append(diagnostics, &clustdiags.ClusterRoleBindings{ClusterRoleBindingsClient: clusterClient, SARClient: clusterClient})
56 58
 
57 59
 		default:
58 60
 			return nil, false, fmt.Errorf("unknown diagnostic: %v", diagnosticName)
59 61
new file mode 100644
... ...
@@ -0,0 +1,99 @@
0
+package cluster
1
+
2
+import (
3
+	"fmt"
4
+	"io/ioutil"
5
+
6
+	kerrs "k8s.io/kubernetes/pkg/api/errors"
7
+
8
+	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
9
+	osclient "github.com/openshift/origin/pkg/client"
10
+	policycmd "github.com/openshift/origin/pkg/cmd/admin/policy"
11
+	"github.com/openshift/origin/pkg/diagnostics/types"
12
+)
13
+
14
+// ClusterRoleBindings is a Diagnostic to check that the default cluster role bindings match expectations
15
+type ClusterRoleBindings struct {
16
+	ClusterRoleBindingsClient osclient.ClusterRoleBindingsInterface
17
+	SARClient                 osclient.SubjectAccessReviews
18
+}
19
+
20
+const (
21
+	ClusterRoleBindingsName = "ClusterRoleBindings"
22
+)
23
+
24
+func (d *ClusterRoleBindings) Name() string {
25
+	return ClusterRoleBindingsName
26
+}
27
+
28
+func (d *ClusterRoleBindings) Description() string {
29
+	return "Check that the ClusterRoleBindings are up-to-date"
30
+}
31
+
32
+func (d *ClusterRoleBindings) CanRun() (bool, error) {
33
+	if d.ClusterRoleBindingsClient == nil {
34
+		return false, fmt.Errorf("must have client.ClusterRoleBindingsInterface")
35
+	}
36
+	if d.SARClient == nil {
37
+		return false, fmt.Errorf("must have client.SubjectAccessReviews")
38
+	}
39
+
40
+	return userCan(d.SARClient, authorizationapi.AuthorizationAttributes{
41
+		Verb:     "list",
42
+		Resource: "clusterrolebindings",
43
+	})
44
+}
45
+
46
+func (d *ClusterRoleBindings) Check() types.DiagnosticResult {
47
+	r := types.NewDiagnosticResult(ClusterRoleBindingsName)
48
+
49
+	reconcileOptions := &policycmd.ReconcileClusterRoleBindingsOptions{
50
+		Confirmed:         false,
51
+		Union:             false,
52
+		Out:               ioutil.Discard,
53
+		RoleBindingClient: d.ClusterRoleBindingsClient.ClusterRoleBindings(),
54
+	}
55
+
56
+	changedClusterRoleBindings, err := reconcileOptions.ChangedClusterRoleBindings()
57
+	if err != nil {
58
+		r.Error("CRBD1000", err, fmt.Sprintf("Error inspecting ClusterRoleBindings: %v", err))
59
+	}
60
+
61
+	// success
62
+	if len(changedClusterRoleBindings) == 0 {
63
+		return r
64
+	}
65
+
66
+	for _, changedClusterRoleBinding := range changedClusterRoleBindings {
67
+		actualClusterRole, err := d.ClusterRoleBindingsClient.ClusterRoleBindings().Get(changedClusterRoleBinding.Name)
68
+		if kerrs.IsNotFound(err) {
69
+			r.Error("CRBD1001", nil, fmt.Sprintf("clusterrolebinding/%s is missing.\n\nUse the `oadm policy reconcile-cluster-role-bindings` command to create the role binding.", changedClusterRoleBinding.Name))
70
+			continue
71
+		}
72
+		if err != nil {
73
+			r.Error("CRBD1002", err, fmt.Sprintf("Unable to get clusterrolebinding/%s: %v", changedClusterRoleBinding.Name, err))
74
+		}
75
+
76
+		missingSubjects, extraSubjects := policycmd.Diff(changedClusterRoleBinding.Subjects, actualClusterRole.Subjects)
77
+		switch {
78
+		case len(missingSubjects) > 0:
79
+			// Only a warning, because they can remove things like self-provisioner role from system:unauthenticated, and it's not an error
80
+			r.Warn("CRBD1003", nil, fmt.Sprintf("clusterrolebinding/%s is missing expected subjects.\n\nUse the `oadm policy reconcile-cluster-role bindings` command to update the role binding to include expected subjects.", changedClusterRoleBinding.Name))
81
+		case len(extraSubjects) > 0:
82
+			// Only info, because it is normal to use policy to grant cluster roles to users
83
+			r.Info("CRBD1004", fmt.Sprintf("clusterrolebinding/%s has more subjects than expected.\n\nUse the `oadm policy reconcile-cluster-role bindings` command to update the role binding to remove extra subjects.", changedClusterRoleBinding.Name))
84
+		}
85
+
86
+		for _, missingSubject := range missingSubjects {
87
+			r.Info("CRBD1005", fmt.Sprintf("clusterrolebinding/%s is missing subject %v.", changedClusterRoleBinding.Name, missingSubject))
88
+		}
89
+
90
+		for _, extraSubject := range extraSubjects {
91
+			r.Info("CRBD1006", fmt.Sprintf("clusterrolebinding/%s has extra subject %v.", changedClusterRoleBinding.Name, extraSubject))
92
+		}
93
+
94
+		r.Debug("CRBD1007", fmt.Sprintf("clusterrolebinding/%s is now %v.", changedClusterRoleBinding.Name, changedClusterRoleBinding))
95
+	}
96
+
97
+	return r
98
+}