Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ <h1>JavaScript Required</h1>
<script src="scripts/services/convert.js"></script>
<script src="scripts/services/breadcrumbs.js"></script>
<script src="scripts/services/quota.js"></script>
<script src="scripts/services/securityCheck.js"></script>
<script src="scripts/services/labels.js"></script>
<script src="scripts/services/catalog.js"></script>
<script src="scripts/services/modals.js"></script>
Expand Down
30 changes: 30 additions & 0 deletions app/scripts/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,36 @@ window.OPENSHIFT_CONSTANTS = {
// 'openshift' should always be included
CREATE_FROM_URL_WHITELIST: ['openshift'],

// Namespaced resources not in this whitelist will be flagged to users as potential concerns in template processing
// and Import YAML/JSON. This typically shouldn't be customized but can be if necessary.
SECURITY_CHECK_WHITELIST: [
{resource: 'buildconfigs', group: ''},
{resource: 'builds', group: ''},
{resource: 'configmaps', group: ''},
{resource: 'daemonsets', group: 'extensions'},
{resource: 'deployments', group: 'extensions'},
{resource: 'deploymentconfigs', group: ''},
{resource: 'endpoints', group: ''},
{resource: 'events', group: ''},
{resource: 'horizontalpodautoscalers', group: 'autoscaling'},
{resource: 'horizontalpodautoscalers', group: 'extensions'},
{resource: 'imagestreamimages', group: ''},
{resource: 'imagestreams', group: ''},
{resource: 'imagestreamtags', group: ''},
{resource: 'ingresses', group: 'extensions'},
{resource: 'jobs', group: 'batch'},
{resource: 'persistentvolumeclaims', group: ''},
{resource: 'pods', group: ''},
{resource: 'podtemplates', group: ''},
{resource: 'replicasets', group: 'extensions'},
{resource: 'replicationcontrollers', group: ''},
{resource: 'routes', group: ''},
{resource: 'secrets', group: ''},
{resource: 'serviceaccounts', group: ''},
{resource: 'services', group: ''},
{resource: 'statefulsets', group: 'apps'}
],

// href's will be prefixed with /project/{{projectName}} unless they are absolute URLs
PROJECT_NAVIGATION: [
{
Expand Down
16 changes: 10 additions & 6 deletions app/scripts/controllers/newfromtemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ angular.module('openshiftConsole')
AlertMessageService,
ProjectsService,
QuotaService,
SecurityCheckService,
$q,
$location,
TaskList,
Expand All @@ -42,7 +43,7 @@ angular.module('openshiftConsole')
}

$scope.alerts = {};
$scope.quotaAlerts = {};
$scope.precheckAlerts = {};
$scope.projectName = $routeParams.project;
$scope.projectPromise = $.Deferred();
$scope.labels = [];
Expand Down Expand Up @@ -292,7 +293,7 @@ angular.module('openshiftConsole')
modalConfig: function() {
return {
alerts: alerts,
message: "Problems were detected while checking your application configuration.",
message: "We checked your application for potential problems. Please confirm you still want to create this application.",
okButtonText: "Create Anyway",
okButtonClass: "btn-danger",
cancelButtonText: "Cancel"
Expand All @@ -305,15 +306,18 @@ angular.module('openshiftConsole')
};

var showWarningsOrCreate = function(result) {
var alerts = SecurityCheckService.getSecurityAlerts(processedResources, $scope.projectName);

// Now that all checks are completed, show any Alerts if we need to
var quotaAlerts = result.quotaAlerts || [];
var errorAlerts = _.filter(quotaAlerts, {type: 'error'});
alerts = alerts.concat(quotaAlerts);
var errorAlerts = _.filter(alerts, {type: 'error'});
if (errorAlerts.length) {
$scope.disableInputs = false;
$scope.quotaAlerts = quotaAlerts;
$scope.precheckAlerts = alerts;
}
else if (quotaAlerts.length) {
launchConfirmationDialog(quotaAlerts);
else if (alerts.length) {
launchConfirmationDialog(alerts);
$scope.disableInputs = false;
}
else {
Expand Down
18 changes: 11 additions & 7 deletions app/scripts/directives/fromFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ angular.module("openshiftConsole")
TaskList,
DataService,
APIService,
QuotaService) {
QuotaService,
SecurityCheckService) {
return {
restrict: "E",
scope: false,
Expand Down Expand Up @@ -62,7 +63,7 @@ angular.module("openshiftConsole")
modalConfig: function() {
return {
alerts: alerts,
message: "Problems were detected while checking your application configuration.",
message: "We checked your application for potential problems. Please confirm you still want to create this application.",
okButtonText: "Create Anyway",
okButtonClass: "btn-danger",
cancelButtonText: "Cancel"
Expand All @@ -75,15 +76,18 @@ angular.module("openshiftConsole")
};

var showWarningsOrCreate = function(result){
var alerts = SecurityCheckService.getSecurityAlerts($scope.createResources, $scope.projectName);

// Now that all checks are completed, show any Alerts if we need to
var quotaAlerts = result.quotaAlerts || [];
var errorAlerts = _.filter(quotaAlerts, {type: 'error'});
alerts = alerts.concat(quotaAlerts);
var errorAlerts = _.filter(alerts, {type: 'error'});
if (errorAlerts.length) {
$scope.disableInputs = false;
$scope.alerts = quotaAlerts;
$scope.alerts = alerts;
}
else if (quotaAlerts.length) {
launchConfirmationDialog(quotaAlerts);
else if (alerts.length) {
launchConfirmationDialog(alerts);
$scope.disableInputs = false;
}
else {
Expand Down Expand Up @@ -155,7 +159,7 @@ angular.module("openshiftConsole")
if ($scope.errorOccured) {
return;
}
// If resource if Template and it doesn't exist in the project
// If resource is Template and it doesn't exist in the project
if ($scope.createResources.length === 1 && $scope.resourceList[0].kind === "Template") {
openTemplateProcessModal();
// Else if any resources already exist
Expand Down
90 changes: 90 additions & 0 deletions app/scripts/services/securityCheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use strict';

angular.module("openshiftConsole")
.factory("SecurityCheckService", function(APIService, $filter, Constants) {
var humanizeKind = $filter('humanizeKind');
var getSecurityAlerts = function(resources, project) {
var alerts = [];
var clusterScopedResources = [];
var roleBindingResources = [];
var roleResources = [];
var notWhitelistedResources = [];
_.each(resources, function(resource) {
if (!_.get(resource, "kind")) {
// This isn't a valid API object
return;
}
var rgv = APIService.objectToResourceGroupVersion(resource);
var apiInfo = APIService.apiInfo(rgv);
if (!apiInfo.namespaced) {
clusterScopedResources.push(resource);
}
else if (rgv.resource === "rolebindings" && (rgv.group === '' || rgv.group === "rbac.authorization.k8s.io")) {
// If role in the rolebinding is one of the "safe" ones ignore it (view or image puller), otherwise warn
var roleRef = _.get(resource, 'roleRef.name');
if (roleRef !== 'view' && roleRef !== 'system:image-puller') {
roleBindingResources.push(resource);
}
}
else if (rgv.resource === "roles" && (rgv.group === '' || rgv.group === "rbac.authorization.k8s.io")) {
roleResources.push(resource);
}
else if (!_.find(Constants.SECURITY_CHECK_WHITELIST, {resource: rgv.resource, group: rgv.group})) {
notWhitelistedResources.push(resource);
}
});
if (clusterScopedResources.length) {
var clusterStrs = _.uniq(_.map(clusterScopedResources, function(resource) {
return humanizeKind(resource.kind);
}));
alerts.push({
type: 'warning',
message: "This will create resources outside of the project, which might impact all users of the cluster.",
details: "Typically only cluster administrators can create these resources. The cluster-level resources being created are: " + clusterStrs.join(", ")
});
}
if (roleBindingResources.length) {
var roleBindingStrs = [];
_.each(roleBindingResources, function(resource){
_.each(resource.subjects, function(subject) {
var str = humanizeKind(subject.kind) + " ";
if (subject.kind === 'ServiceAccount') {
str += (subject.namespace || project) + "/";
}
str += subject.name;
roleBindingStrs.push(str);
});
});
roleBindingStrs = _.uniq(roleBindingStrs);
alerts.push({
type: 'warning',
message: "This will grant permissions to your project.",
details: "Permissions are being granted to: " + roleBindingStrs.join(", ")
});
}
if (roleResources.length) {
alerts.push({
type: 'info',
message: "This will create additional membership roles within the project.",
details: "Admins will be able to grant these custom roles to users, groups, and service accounts."
});
}
if (notWhitelistedResources.length) {
var notWhitelistStrs = _.uniq(_.map(notWhitelistedResources, function(resource) {
return humanizeKind(resource.kind);
}));
alerts.push({
type: 'warning',
message: "This will create resources that may have security or project behavior implications.",
details: "Make sure you understand what they do before creating them. The resources being created are: " + notWhitelistStrs.join(", ")
});
}
return alerts;
};

return {
// Gets security alerts relevant to a set of resources
// Returns: Array of alerts
getSecurityAlerts: getSecurityAlerts
};
});
2 changes: 1 addition & 1 deletion app/views/newfromtemplate.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ <h2>Images</h2>
can-toggle="false"
help-text="Each label is applied to each created resource.">
</label-editor>
<alerts alerts="quotaAlerts"></alerts>
<alerts alerts="precheckAlerts"></alerts>
<div class="buttons gutter-top-bottom">
<button class="btn btn-primary btn-lg" ng-click="createFromTemplate()" ng-disabled="templateForm.$invalid || disableInputs">Create</button>
<a class="btn btn-default btn-lg" href="{{projectName | projectOverviewURL}}">Cancel</a>
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"angular-utf8-base64": "0.0.5",
"file-saver": "1.3.3",
"bootstrap-switch": "3.3.3",
"origin-web-common": "0.0.3"
"origin-web-common": "0.0.5"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to bump this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, won't be able to create cluster resources without this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right of course :)

},
"devDependencies": {
"angular-mocks": "1.5.11",
Expand Down
Loading