... | ... |
@@ -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 |
+} |