Skip to content

Commit cb3edfd

Browse files
committed
Security health check helper improvements and minor updates to roles
1 parent dea3092 commit cb3edfd

File tree

13 files changed

+464
-203
lines changed

13 files changed

+464
-203
lines changed

kubernetes/internal/generate-security-policy.sh

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,11 @@ metadata:
9494
weblogic.operatorName: ${NAMESPACE}
9595
rules:
9696
- apiGroups: [""]
97-
resources: ["namespaces", "persistentvolumes"]
97+
resources: ["namespaces"]
9898
verbs: ["get", "list", "watch"]
99+
- apiGroups: [""]
100+
resources: ["persistentvolumes"]
101+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"]
99102
- apiGroups: ["apiextensions.k8s.io"]
100103
resources: ["customresourcedefinitions"]
101104
verbs: ["get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"]
@@ -108,6 +111,12 @@ rules:
108111
- apiGroups: ["extensions"]
109112
resources: ["ingresses"]
110113
verbs: ["get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"]
114+
- apiGroups: ["authentication.k8s.io"]
115+
resources: ["tokenreviews"]
116+
verbs: ["create"]
117+
- apiGroups: ["authorization.k8s.io"]
118+
resources: ["selfsubjectaccessreviews", "localsubjectaccessreviews", "subjectaccessreviews", "selfsubjectrulesreviews"]
119+
verbs: ["create"]
111120
---
112121
kind: ClusterRole
113122
apiVersion: rbac.authorization.k8s.io/v1beta1
@@ -197,20 +206,23 @@ metadata:
197206
weblogic.operatorName: ${NAMESPACE}
198207
rules:
199208
- apiGroups: [""]
200-
resources: ["secrets", "persistentvolumeclaims"]
209+
resources: ["secrets"]
201210
verbs: ["get", "list", "watch"]
202211
- apiGroups: ["storage.k8s.io"]
203212
resources: ["storageclasses"]
204213
verbs: ["get", "list", "watch"]
205214
- apiGroups: [""]
206-
resources: ["services", "configmaps", "pods", "jobs", "events"]
215+
resources: ["services", "configmaps", "pods", "podtemplates", "events", "persistentvolumeclaims"]
207216
verbs: ["get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"]
208217
- apiGroups: [""]
209218
resources: ["pods/logs"]
210219
verbs: ["get", "list"]
211220
- apiGroups: [""]
212221
resources: ["pods/exec"]
213222
verbs: ["create"]
223+
- apiGroups: ["batch"]
224+
resources: ["jobs", "cronjobs"]
225+
verbs: ["get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"]
214226
- apiGroups: ["settings.k8s.io"]
215227
resources: ["podpresets"]
216228
verbs: ["get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"]
@@ -253,4 +265,4 @@ EOF
253265
#
254266
echo "Create the WebLogic Operator Security configuration using kubectl as follows: kubectl create -f ${SCRIPT}"
255267
#
256-
echo "Ensure you start the API server with the --authorization-mode=RBAC option."
268+
echo "Ensure you start the API server with the --authorization-mode=RBAC option."

operator/src/main/java/oracle/kubernetes/operator/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ private static void begin() {
220220
HealthCheckHelper healthCheck = new HealthCheckHelper(namespace, targetNamespaces);
221221
version = healthCheck.performK8sVersionCheck();
222222
healthCheck.performNonSecurityChecks();
223-
healthCheck.performSecurityChecks(serviceAccountName);
223+
healthCheck.performSecurityChecks();
224224
} catch (ApiException e) {
225225
LOGGER.warning(MessageKeys.EXCEPTION, e);
226226
}

operator/src/main/java/oracle/kubernetes/operator/helpers/AuthenticationProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public V1TokenReviewStatus check(String principal, String token) {
3636
try {
3737
boolean allowed = authorizationProxy.check(principal,
3838
AuthorizationProxy.Operation.create,
39-
AuthorizationProxy.Resource.tokenreviews,
39+
AuthorizationProxy.Resource.TOKENREVIEWS,
4040
null,
4141
AuthorizationProxy.Scope.cluster,
4242
null);

operator/src/main/java/oracle/kubernetes/operator/helpers/AuthorizationProxy.java

Lines changed: 82 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import io.kubernetes.client.ApiException;
99
import io.kubernetes.client.models.V1ObjectMeta;
1010
import io.kubernetes.client.models.V1ResourceAttributes;
11+
import io.kubernetes.client.models.V1SelfSubjectAccessReview;
12+
import io.kubernetes.client.models.V1SelfSubjectAccessReviewSpec;
1113
import io.kubernetes.client.models.V1SubjectAccessReview;
1214
import io.kubernetes.client.models.V1SubjectAccessReviewSpec;
1315
import io.kubernetes.client.models.V1SubjectAccessReviewStatus;
@@ -37,18 +39,50 @@ public enum Operation {
3739
}
3840

3941
public enum Resource {
40-
pods,
41-
services,
42-
namespaces,
43-
customresources,
44-
customresourcedefinitions,
45-
domains,
46-
tokenreviews,
47-
networkpolicies,
48-
secrets,
49-
persistentvolumes,
50-
persistentvolumeclaims,
51-
ingresses
42+
CONFIGMAPS ("configmaps", ""),
43+
PODS ("pods", ""),
44+
LOGS ("pods", "logs", ""),
45+
EXEC ("pods", "exec", ""),
46+
PODTEMPLATES ("podtemplates", ""),
47+
EVENTS ("events", ""),
48+
SERVICES ("services", ""),
49+
NAMESPACES ("namespaces", ""),
50+
JOBS ("jobs", "batch"),
51+
CRONJOBS ("cronjobs", "batch"),
52+
CRDS ("customresourcedefinitions", "apiextensions.k8s.io"),
53+
DOMAINS ("domains", "weblogic.oracle"),
54+
DOMAINSTATUSS ("domains", "status", "weblogic.oracle"),
55+
SUBJECTACCESSREVIEWS ("subjectaccessreviews", "authorization.k8s.io"),
56+
SELFSUBJECTACCESSREVIEWS ("selfsubjectaccessreviews", "authorization.k8s.io"),
57+
LOCALSUBJECTACCESSREVIEWS ("localsubjectaccessreviews", "authorization.k8s.io"),
58+
SELFSUBJECTRULESREVIEWS ("selfsubjectrulesreviews", "authorization.k8s.io"),
59+
TOKENREVIEWS ("tokenreviews", "authentication.k8s.io"),
60+
SECRETS ("secrets", ""),
61+
PERSISTENTVOLUMES ("persistentvolumes", ""),
62+
PERSISTENTVOLUMECLAIMS ("persistentvolumeclaims", ""),
63+
STORAGECLASSES ("storageclasses", "storage.k8s.io"),
64+
PODPRESETS ("podpresets" , "settings.k8s.io"),
65+
INGRESSES ("ingresses", "extensions"),
66+
NETWORKPOLICIES ("networkpolicies", "extensions"),
67+
PODSECURITYPOLICIES ("podsecuritypolicies", "extensions");
68+
69+
private final String resource;
70+
private final String subResource;
71+
private final String apiGroup;
72+
73+
Resource(String resource, String apiGroup) {
74+
this(resource, "", apiGroup);
75+
}
76+
77+
Resource(String resource, String subResource, String apiGroup) {
78+
this.resource = resource;
79+
this.subResource = subResource;
80+
this.apiGroup = apiGroup;
81+
}
82+
83+
public String getResource() { return resource; }
84+
public String getSubResource() { return subResource; }
85+
public String getAPIGroup() { return apiGroup; }
5286
}
5387

5488
public enum Scope {
@@ -104,6 +138,24 @@ public boolean check(String principal, final List<String> groups, Operation oper
104138
return result;
105139
}
106140

141+
public boolean check(Operation operation, Resource resource, String resourceName, Scope scope, String namespaceName) {
142+
LOGGER.entering();
143+
V1SelfSubjectAccessReview subjectAccessReview = prepareSelfSubjectAccessReview(operation, resource, resourceName, scope, namespaceName);
144+
try {
145+
CallBuilderFactory factory = ContainerResolver.getInstance().getContainer().getSPI(CallBuilderFactory.class);
146+
subjectAccessReview = factory.create().createSelfSubjectAccessReview(subjectAccessReview);
147+
} catch (ApiException e) {
148+
LOGGER.severe(MessageKeys.APIEXCEPTION_FROM_SUBJECT_ACCESS_REVIEW, e);
149+
LOGGER.exiting(Boolean.FALSE);
150+
return Boolean.FALSE;
151+
152+
}
153+
V1SubjectAccessReviewStatus subjectAccessReviewStatus = subjectAccessReview.getStatus();
154+
Boolean result = subjectAccessReviewStatus.isAllowed();
155+
LOGGER.exiting(result);
156+
return result;
157+
}
158+
107159
/**
108160
* Prepares an instance of SubjectAccessReview and returns same.
109161
*
@@ -133,6 +185,21 @@ private V1SubjectAccessReview prepareSubjectAccessReview(String principal, final
133185
return subjectAccessReview;
134186
}
135187

188+
private V1SelfSubjectAccessReview prepareSelfSubjectAccessReview(Operation operation, Resource resource, String resourceName, Scope scope, String namespaceName) {
189+
LOGGER.entering();
190+
V1SelfSubjectAccessReviewSpec subjectAccessReviewSpec = new V1SelfSubjectAccessReviewSpec();
191+
192+
subjectAccessReviewSpec.setResourceAttributes(prepareResourceAttributes(operation, resource, resourceName, scope, namespaceName));
193+
194+
V1SelfSubjectAccessReview subjectAccessReview = new V1SelfSubjectAccessReview();
195+
subjectAccessReview.setApiVersion("authorization.k8s.io/v1");
196+
subjectAccessReview.setKind("SelfSubjectAccessReview");
197+
subjectAccessReview.setMetadata(new V1ObjectMeta());
198+
subjectAccessReview.setSpec(subjectAccessReviewSpec);
199+
LOGGER.exiting(subjectAccessReview);
200+
return subjectAccessReview;
201+
}
202+
136203
/**
137204
* Prepares an instance of ResourceAttributes and returns same.
138205
*
@@ -150,12 +217,9 @@ private V1ResourceAttributes prepareResourceAttributes(Operation operation, Reso
150217
resourceAttributes.setVerb(operation.toString());
151218
}
152219
if (null != resource) {
153-
resourceAttributes.setResource(resource.toString());
154-
}
155-
156-
String apiGroup = getApiGroup(resource);
157-
if (apiGroup != null) {
158-
resourceAttributes.setGroup(apiGroup);
220+
resourceAttributes.setResource(resource.resource);
221+
resourceAttributes.setSubresource(resource.subResource);
222+
resourceAttributes.setGroup(resource.apiGroup);
159223
}
160224

161225
if (null != resourceName) {
@@ -168,25 +232,4 @@ private V1ResourceAttributes prepareResourceAttributes(Operation operation, Reso
168232
LOGGER.exiting(resourceAttributes);
169233
return resourceAttributes;
170234
}
171-
172-
private String getApiGroup(Resource resource) {
173-
if (resource == Resource.domains) {
174-
return "weblogic.oracle";
175-
}
176-
177-
if (resource == Resource.customresourcedefinitions) {
178-
return "apiextensions.k8s.io";
179-
}
180-
181-
if (resource == Resource.tokenreviews) {
182-
return "authentication.k8s.io";
183-
}
184-
185-
if (resource == Resource.ingresses) {
186-
return "extensions";
187-
}
188-
189-
// TODO - do we need to specify the api group for any of the other Resource values?
190-
return null;
191-
}
192235
}

operator/src/main/java/oracle/kubernetes/operator/helpers/CallBuilder.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import io.kubernetes.client.models.V1Pod;
3737
import io.kubernetes.client.models.V1PodList;
3838
import io.kubernetes.client.models.V1Secret;
39+
import io.kubernetes.client.models.V1SelfSubjectAccessReview;
3940
import io.kubernetes.client.models.V1Service;
4041
import io.kubernetes.client.models.V1ServiceList;
4142
import io.kubernetes.client.models.V1Status;
@@ -1384,6 +1385,41 @@ public Step createSubjectAccessReviewAsync(V1SubjectAccessReview body, ResponseS
13841385
return createRequestAsync(responseStep, new RequestParams("createSubjectAccessReview", null, null, body), CREATE_SUBJECTACCESSREVIEW);
13851386
}
13861387

1388+
/* Self Subject Access Review */
1389+
1390+
/**
1391+
* Create self subject access review
1392+
* @param body Body
1393+
* @return Created self subject access review
1394+
* @throws ApiException API Exception
1395+
*/
1396+
public V1SelfSubjectAccessReview createSelfSubjectAccessReview(V1SelfSubjectAccessReview body) throws ApiException {
1397+
ApiClient client = helper.take();
1398+
try {
1399+
return new AuthorizationV1Api(client).createSelfSubjectAccessReview(body, pretty);
1400+
} finally {
1401+
helper.recycle(client);
1402+
}
1403+
}
1404+
1405+
private com.squareup.okhttp.Call createSelfSubjectAccessReviewAsync(ApiClient client, V1SelfSubjectAccessReview body, ApiCallback<V1SelfSubjectAccessReview> callback) throws ApiException {
1406+
return new AuthorizationV1Api(client).createSelfSubjectAccessReviewAsync(body, pretty, callback);
1407+
}
1408+
1409+
private final CallFactory<V1SelfSubjectAccessReview> CREATE_SELFSUBJECTACCESSREVIEW = (requestParams, usage, cont, callback) -> {
1410+
return createSelfSubjectAccessReviewAsync(usage, (V1SelfSubjectAccessReview) requestParams.body, callback);
1411+
};
1412+
1413+
/**
1414+
* Asynchronous step for creating self subject access review
1415+
* @param body Body
1416+
* @param responseStep Response step for when call completes
1417+
* @return Asynchronous step
1418+
*/
1419+
public Step createSelfSubjectAccessReviewAsync(V1SelfSubjectAccessReview body, ResponseStep<V1SelfSubjectAccessReview> responseStep) {
1420+
return createRequestAsync(responseStep, new RequestParams("createSelfSubjectAccessReview", null, null, body), CREATE_SELFSUBJECTACCESSREVIEW);
1421+
}
1422+
13871423
/* Token Review */
13881424

13891425
/**
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2018, Oracle Corporation and/or its affiliates. All rights reserved.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
3+
4+
package oracle.kubernetes.operator.helpers;
5+
6+
import java.util.function.Supplier;
7+
8+
import io.kubernetes.client.ApiClient;
9+
10+
public interface ClientFactory extends Supplier<ApiClient> {
11+
12+
}

operator/src/main/java/oracle/kubernetes/operator/helpers/ClientPool.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@
99
import oracle.kubernetes.operator.logging.LoggingFacade;
1010
import oracle.kubernetes.operator.logging.LoggingFactory;
1111
import oracle.kubernetes.operator.logging.MessageKeys;
12+
import oracle.kubernetes.operator.work.Container;
13+
import oracle.kubernetes.operator.work.ContainerResolver;
1214

15+
import java.io.IOException;
1316
import java.util.concurrent.TimeUnit;
1417
import java.util.concurrent.atomic.AtomicBoolean;
1518

1619
public class ClientPool extends Pool<ApiClient> {
1720
private static final LoggingFacade LOGGER = LoggingFactory.getLogger("Operator", "Operator");
1821
private static final ClientPool SINGLETON = new ClientPool();
19-
20-
private final AtomicBoolean first = new AtomicBoolean(true);
22+
23+
private static final DefaultClientFactory FACTORY = new DefaultClientFactory();
2124

2225
public static ClientPool getInstance() {
2326
return SINGLETON;
@@ -37,10 +40,16 @@ private ApiClient getApiClient() {
3740
ApiClient client = null;
3841
LOGGER.fine(MessageKeys.CREATING_API_CLIENT);
3942
try {
40-
client = Config.defaultClient();
41-
if (first.getAndSet(false)) {
42-
Configuration.setDefaultApiClient(client);
43+
ClientFactory factory = null;
44+
Container c = ContainerResolver.getInstance().getContainer();
45+
if (c != null) {
46+
factory = c.getSPI(ClientFactory.class);
47+
}
48+
if (factory == null) {
49+
factory = FACTORY;
4350
}
51+
52+
client = factory.get();
4453
} catch (Throwable e) {
4554
LOGGER.warning(MessageKeys.EXCEPTION, e);
4655
}
@@ -53,4 +62,22 @@ private ApiClient getApiClient() {
5362
return client;
5463
}
5564

65+
private static class DefaultClientFactory implements ClientFactory {
66+
private final AtomicBoolean first = new AtomicBoolean(true);
67+
68+
@Override
69+
public ApiClient get() {
70+
ApiClient client;
71+
try {
72+
client = Config.defaultClient();
73+
if (first.getAndSet(false)) {
74+
Configuration.setDefaultApiClient(client);
75+
}
76+
return client;
77+
} catch (IOException e) {
78+
throw new RuntimeException(e);
79+
}
80+
}
81+
82+
}
5683
}

0 commit comments

Comments
 (0)