Skip to content

Commit 295a6c8

Browse files
author
OpenShift Bot
authoredMar 8, 2017
Merge pull request #1321 from jwforres/security-checks
Merged by openshift-bot
2 parents d733d79 + fb82c39 commit 295a6c8

File tree

10 files changed

+481
-200
lines changed

10 files changed

+481
-200
lines changed
 

‎app/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ <h1>JavaScript Required</h1>
198198
<script src="scripts/services/convert.js"></script>
199199
<script src="scripts/services/breadcrumbs.js"></script>
200200
<script src="scripts/services/quota.js"></script>
201+
<script src="scripts/services/securityCheck.js"></script>
201202
<script src="scripts/services/labels.js"></script>
202203
<script src="scripts/services/catalog.js"></script>
203204
<script src="scripts/services/modals.js"></script>

‎app/scripts/constants.js

+30
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,36 @@ window.OPENSHIFT_CONSTANTS = {
9797
// 'openshift' should always be included
9898
CREATE_FROM_URL_WHITELIST: ['openshift'],
9999

100+
// Namespaced resources not in this whitelist will be flagged to users as potential concerns in template processing
101+
// and Import YAML/JSON. This typically shouldn't be customized but can be if necessary.
102+
SECURITY_CHECK_WHITELIST: [
103+
{resource: 'buildconfigs', group: ''},
104+
{resource: 'builds', group: ''},
105+
{resource: 'configmaps', group: ''},
106+
{resource: 'daemonsets', group: 'extensions'},
107+
{resource: 'deployments', group: 'extensions'},
108+
{resource: 'deploymentconfigs', group: ''},
109+
{resource: 'endpoints', group: ''},
110+
{resource: 'events', group: ''},
111+
{resource: 'horizontalpodautoscalers', group: 'autoscaling'},
112+
{resource: 'horizontalpodautoscalers', group: 'extensions'},
113+
{resource: 'imagestreamimages', group: ''},
114+
{resource: 'imagestreams', group: ''},
115+
{resource: 'imagestreamtags', group: ''},
116+
{resource: 'ingresses', group: 'extensions'},
117+
{resource: 'jobs', group: 'batch'},
118+
{resource: 'persistentvolumeclaims', group: ''},
119+
{resource: 'pods', group: ''},
120+
{resource: 'podtemplates', group: ''},
121+
{resource: 'replicasets', group: 'extensions'},
122+
{resource: 'replicationcontrollers', group: ''},
123+
{resource: 'routes', group: ''},
124+
{resource: 'secrets', group: ''},
125+
{resource: 'serviceaccounts', group: ''},
126+
{resource: 'services', group: ''},
127+
{resource: 'statefulsets', group: 'apps'}
128+
],
129+
100130
// href's will be prefixed with /project/{{projectName}} unless they are absolute URLs
101131
PROJECT_NAVIGATION: [
102132
{

‎app/scripts/controllers/newfromtemplate.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ angular.module('openshiftConsole')
1818
AlertMessageService,
1919
ProjectsService,
2020
QuotaService,
21+
SecurityCheckService,
2122
$q,
2223
$location,
2324
TaskList,
@@ -42,7 +43,7 @@ angular.module('openshiftConsole')
4243
}
4344

4445
$scope.alerts = {};
45-
$scope.quotaAlerts = {};
46+
$scope.precheckAlerts = {};
4647
$scope.projectName = $routeParams.project;
4748
$scope.projectPromise = $.Deferred();
4849
$scope.labels = [];
@@ -292,7 +293,7 @@ angular.module('openshiftConsole')
292293
modalConfig: function() {
293294
return {
294295
alerts: alerts,
295-
message: "Problems were detected while checking your application configuration.",
296+
message: "We checked your application for potential problems. Please confirm you still want to create this application.",
296297
okButtonText: "Create Anyway",
297298
okButtonClass: "btn-danger",
298299
cancelButtonText: "Cancel"
@@ -305,15 +306,18 @@ angular.module('openshiftConsole')
305306
};
306307

307308
var showWarningsOrCreate = function(result) {
309+
var alerts = SecurityCheckService.getSecurityAlerts(processedResources, $scope.projectName);
310+
308311
// Now that all checks are completed, show any Alerts if we need to
309312
var quotaAlerts = result.quotaAlerts || [];
310-
var errorAlerts = _.filter(quotaAlerts, {type: 'error'});
313+
alerts = alerts.concat(quotaAlerts);
314+
var errorAlerts = _.filter(alerts, {type: 'error'});
311315
if (errorAlerts.length) {
312316
$scope.disableInputs = false;
313-
$scope.quotaAlerts = quotaAlerts;
317+
$scope.precheckAlerts = alerts;
314318
}
315-
else if (quotaAlerts.length) {
316-
launchConfirmationDialog(quotaAlerts);
319+
else if (alerts.length) {
320+
launchConfirmationDialog(alerts);
317321
$scope.disableInputs = false;
318322
}
319323
else {

‎app/scripts/directives/fromFile.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ angular.module("openshiftConsole")
1111
TaskList,
1212
DataService,
1313
APIService,
14-
QuotaService) {
14+
QuotaService,
15+
SecurityCheckService) {
1516
return {
1617
restrict: "E",
1718
scope: false,
@@ -62,7 +63,7 @@ angular.module("openshiftConsole")
6263
modalConfig: function() {
6364
return {
6465
alerts: alerts,
65-
message: "Problems were detected while checking your application configuration.",
66+
message: "We checked your application for potential problems. Please confirm you still want to create this application.",
6667
okButtonText: "Create Anyway",
6768
okButtonClass: "btn-danger",
6869
cancelButtonText: "Cancel"
@@ -75,15 +76,18 @@ angular.module("openshiftConsole")
7576
};
7677

7778
var showWarningsOrCreate = function(result){
79+
var alerts = SecurityCheckService.getSecurityAlerts($scope.createResources, $scope.projectName);
80+
7881
// Now that all checks are completed, show any Alerts if we need to
7982
var quotaAlerts = result.quotaAlerts || [];
80-
var errorAlerts = _.filter(quotaAlerts, {type: 'error'});
83+
alerts = alerts.concat(quotaAlerts);
84+
var errorAlerts = _.filter(alerts, {type: 'error'});
8185
if (errorAlerts.length) {
8286
$scope.disableInputs = false;
83-
$scope.alerts = quotaAlerts;
87+
$scope.alerts = alerts;
8488
}
85-
else if (quotaAlerts.length) {
86-
launchConfirmationDialog(quotaAlerts);
89+
else if (alerts.length) {
90+
launchConfirmationDialog(alerts);
8791
$scope.disableInputs = false;
8892
}
8993
else {
@@ -155,7 +159,7 @@ angular.module("openshiftConsole")
155159
if ($scope.errorOccured) {
156160
return;
157161
}
158-
// If resource if Template and it doesn't exist in the project
162+
// If resource is Template and it doesn't exist in the project
159163
if ($scope.createResources.length === 1 && $scope.resourceList[0].kind === "Template") {
160164
openTemplateProcessModal();
161165
// Else if any resources already exist

‎app/scripts/services/securityCheck.js

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use strict';
2+
3+
angular.module("openshiftConsole")
4+
.factory("SecurityCheckService", function(APIService, $filter, Constants) {
5+
var humanizeKind = $filter('humanizeKind');
6+
var getSecurityAlerts = function(resources, project) {
7+
var alerts = [];
8+
var clusterScopedResources = [];
9+
var roleBindingResources = [];
10+
var roleResources = [];
11+
var notWhitelistedResources = [];
12+
_.each(resources, function(resource) {
13+
if (!_.get(resource, "kind")) {
14+
// This isn't a valid API object
15+
return;
16+
}
17+
var rgv = APIService.objectToResourceGroupVersion(resource);
18+
var apiInfo = APIService.apiInfo(rgv);
19+
if (!apiInfo.namespaced) {
20+
clusterScopedResources.push(resource);
21+
}
22+
else if (rgv.resource === "rolebindings" && (rgv.group === '' || rgv.group === "rbac.authorization.k8s.io")) {
23+
// If role in the rolebinding is one of the "safe" ones ignore it (view or image puller), otherwise warn
24+
var roleRef = _.get(resource, 'roleRef.name');
25+
if (roleRef !== 'view' && roleRef !== 'system:image-puller') {
26+
roleBindingResources.push(resource);
27+
}
28+
}
29+
else if (rgv.resource === "roles" && (rgv.group === '' || rgv.group === "rbac.authorization.k8s.io")) {
30+
roleResources.push(resource);
31+
}
32+
else if (!_.find(Constants.SECURITY_CHECK_WHITELIST, {resource: rgv.resource, group: rgv.group})) {
33+
notWhitelistedResources.push(resource);
34+
}
35+
});
36+
if (clusterScopedResources.length) {
37+
var clusterStrs = _.uniq(_.map(clusterScopedResources, function(resource) {
38+
return humanizeKind(resource.kind);
39+
}));
40+
alerts.push({
41+
type: 'warning',
42+
message: "This will create resources outside of the project, which might impact all users of the cluster.",
43+
details: "Typically only cluster administrators can create these resources. The cluster-level resources being created are: " + clusterStrs.join(", ")
44+
});
45+
}
46+
if (roleBindingResources.length) {
47+
var roleBindingStrs = [];
48+
_.each(roleBindingResources, function(resource){
49+
_.each(resource.subjects, function(subject) {
50+
var str = humanizeKind(subject.kind) + " ";
51+
if (subject.kind === 'ServiceAccount') {
52+
str += (subject.namespace || project) + "/";
53+
}
54+
str += subject.name;
55+
roleBindingStrs.push(str);
56+
});
57+
});
58+
roleBindingStrs = _.uniq(roleBindingStrs);
59+
alerts.push({
60+
type: 'warning',
61+
message: "This will grant permissions to your project.",
62+
details: "Permissions are being granted to: " + roleBindingStrs.join(", ")
63+
});
64+
}
65+
if (roleResources.length) {
66+
alerts.push({
67+
type: 'info',
68+
message: "This will create additional membership roles within the project.",
69+
details: "Admins will be able to grant these custom roles to users, groups, and service accounts."
70+
});
71+
}
72+
if (notWhitelistedResources.length) {
73+
var notWhitelistStrs = _.uniq(_.map(notWhitelistedResources, function(resource) {
74+
return humanizeKind(resource.kind);
75+
}));
76+
alerts.push({
77+
type: 'warning',
78+
message: "This will create resources that may have security or project behavior implications.",
79+
details: "Make sure you understand what they do before creating them. The resources being created are: " + notWhitelistStrs.join(", ")
80+
});
81+
}
82+
return alerts;
83+
};
84+
85+
return {
86+
// Gets security alerts relevant to a set of resources
87+
// Returns: Array of alerts
88+
getSecurityAlerts: getSecurityAlerts
89+
};
90+
});

‎app/views/newfromtemplate.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ <h2>Images</h2>
5151
can-toggle="false"
5252
help-text="Each label is applied to each created resource.">
5353
</label-editor>
54-
<alerts alerts="quotaAlerts"></alerts>
54+
<alerts alerts="precheckAlerts"></alerts>
5555
<div class="buttons gutter-top-bottom">
5656
<button class="btn btn-primary btn-lg" ng-click="createFromTemplate()" ng-disabled="templateForm.$invalid || disableInputs">Create</button>
5757
<a class="btn btn-default btn-lg" href="{{projectName | projectOverviewURL}}">Cancel</a>

‎bower.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"angular-utf8-base64": "0.0.5",
4848
"file-saver": "1.3.3",
4949
"bootstrap-switch": "3.3.3",
50-
"origin-web-common": "0.0.3"
50+
"origin-web-common": "0.0.5"
5151
},
5252
"devDependencies": {
5353
"angular-mocks": "1.5.11",

0 commit comments

Comments
 (0)
Please sign in to comment.