feat(rbac) remove list ingresses permissions EE-1304 (#5458)

* feat(RBAC) EE-1304 list ingresses of current namespace other than all namespaces at front end side

* feat(RBAC) EE-1304 remove list ingresses from clusterrole

Co-authored-by: Simon Meng <simon.meng@portainer.io>
pull/5560/head
cong meng 2021-09-01 09:43:11 +12:00 committed by GitHub
parent 35013e7b6a
commit 7760595f21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 30 deletions

View File

@ -18,15 +18,10 @@ func getPortainerUserDefaultPolicies() []rbacv1.PolicyRule {
Resources: []string{"storageclasses"}, Resources: []string{"storageclasses"},
APIGroups: []string{"storage.k8s.io"}, APIGroups: []string{"storage.k8s.io"},
}, },
{
Verbs: []string{"list"},
Resources: []string{"ingresses"},
APIGroups: []string{"networking.k8s.io"},
},
} }
} }
func (kcl *KubeClient) createPortainerUserClusterRole() error { func (kcl *KubeClient) upsertPortainerK8sClusterRoles() error {
clusterRole := &rbacv1.ClusterRole{ clusterRole := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: portainerUserCRName, Name: portainerUserCRName,
@ -35,8 +30,13 @@ func (kcl *KubeClient) createPortainerUserClusterRole() error {
} }
_, err := kcl.cli.RbacV1().ClusterRoles().Create(clusterRole) _, err := kcl.cli.RbacV1().ClusterRoles().Create(clusterRole)
if err != nil && !k8serrors.IsAlreadyExists(err) { if err != nil {
return err if k8serrors.IsAlreadyExists(err) {
_, err = kcl.cli.RbacV1().ClusterRoles().Update(clusterRole)
}
if err != nil {
return err
}
} }
return nil return nil

View File

@ -63,7 +63,7 @@ func (kcl *KubeClient) SetupUserServiceAccount(userID int, teamIDs []int, restri
} }
func (kcl *KubeClient) ensureRequiredResourcesExist() error { func (kcl *KubeClient) ensureRequiredResourcesExist() error {
return kcl.createPortainerUserClusterRole() return kcl.upsertPortainerK8sClusterRoles()
} }
func (kcl *KubeClient) createUserServiceAccount(namespace, serviceAccountName string) error { func (kcl *KubeClient) createUserServiceAccount(namespace, serviceAccountName string) error {

View File

@ -1347,7 +1347,7 @@
class="form-control" class="form-control"
name="ingress_class_{{ $index }}" name="ingress_class_{{ $index }}"
ng-model="publishedPort.IngressName" ng-model="publishedPort.IngressName"
ng-options="ingress.Name as ingress.Name for ingress in ctrl.filteredIngresses" ng-options="ingress.Name as ingress.Name for ingress in ctrl.ingresses"
ng-required="!publishedPort.NeedsDeletion" ng-required="!publishedPort.NeedsDeletion"
ng-change="ctrl.onChangePortMappingIngress($index)" ng-change="ctrl.onChangePortMappingIngress($index)"
ng-disabled="ctrl.disableLoadBalancerEdit() || ctrl.isEditAndNotNewPublishedPort($index)" ng-disabled="ctrl.disableLoadBalancerEdit() || ctrl.isEditAndNotNewPublishedPort($index)"

View File

@ -371,7 +371,7 @@ class KubernetesCreateApplicationController {
/* #region PUBLISHED PORTS UI MANAGEMENT */ /* #region PUBLISHED PORTS UI MANAGEMENT */
addPublishedPort() { addPublishedPort() {
const p = new KubernetesApplicationPublishedPortFormValue(); const p = new KubernetesApplicationPublishedPortFormValue();
const ingresses = this.filteredIngresses; const ingresses = this.ingresses;
p.IngressName = ingresses && ingresses.length ? ingresses[0].Name : undefined; p.IngressName = ingresses && ingresses.length ? ingresses[0].Name : undefined;
p.IngressHost = ingresses && ingresses.length ? ingresses[0].Hosts[0] : undefined; p.IngressHost = ingresses && ingresses.length ? ingresses[0].Hosts[0] : undefined;
p.IngressHosts = ingresses && ingresses.length ? ingresses[0].Hosts : undefined; p.IngressHosts = ingresses && ingresses.length ? ingresses[0].Hosts : undefined;
@ -382,7 +382,7 @@ class KubernetesCreateApplicationController {
} }
resetPublishedPorts() { resetPublishedPorts() {
const ingresses = this.filteredIngresses; const ingresses = this.ingresses;
_.forEach(this.formValues.PublishedPorts, (p) => { _.forEach(this.formValues.PublishedPorts, (p) => {
p.IngressName = ingresses && ingresses.length ? ingresses[0].Name : undefined; p.IngressName = ingresses && ingresses.length ? ingresses[0].Name : undefined;
p.IngressHost = ingresses && ingresses.length ? ingresses[0].Hosts[0] : undefined; p.IngressHost = ingresses && ingresses.length ? ingresses[0].Hosts[0] : undefined;
@ -441,7 +441,7 @@ class KubernetesCreateApplicationController {
onChangePortMappingIngress(index) { onChangePortMappingIngress(index) {
const publishedPort = this.formValues.PublishedPorts[index]; const publishedPort = this.formValues.PublishedPorts[index];
const ingress = _.find(this.filteredIngresses, { Name: publishedPort.IngressName }); const ingress = _.find(this.ingresses, { Name: publishedPort.IngressName });
publishedPort.IngressHosts = ingress.Hosts; publishedPort.IngressHosts = ingress.Hosts;
this.ingressHostnames = ingress.Hosts; this.ingressHostnames = ingress.Hosts;
publishedPort.IngressHost = this.ingressHostnames.length ? this.ingressHostnames[0] : []; publishedPort.IngressHost = this.ingressHostnames.length ? this.ingressHostnames[0] : [];
@ -650,7 +650,7 @@ class KubernetesCreateApplicationController {
} }
publishViaIngressEnabled() { publishViaIngressEnabled() {
return this.filteredIngresses.length; return this.ingresses.length;
} }
isEditAndNoChangesMade() { isEditAndNoChangesMade() {
@ -871,16 +871,22 @@ class KubernetesCreateApplicationController {
} }
refreshIngresses(namespace) { refreshIngresses(namespace) {
this.filteredIngresses = _.filter(this.ingresses, { Namespace: namespace }); return this.$async(async () => {
this.ingressHostnames = this.filteredIngresses.length ? this.filteredIngresses[0].Hosts : []; try {
if (!this.publishViaIngressEnabled()) { this.ingresses = await this.KubernetesIngressService.get(namespace);
if (this.savedFormValues) { this.ingressHostnames = this.ingresses.length ? this.ingresses[0].Hosts : [];
this.formValues.PublishingType = this.savedFormValues.PublishingType; if (!this.publishViaIngressEnabled()) {
} else { if (this.savedFormValues) {
this.formValues.PublishingType = this.ApplicationPublishingTypes.INTERNAL; this.formValues.PublishingType = this.savedFormValues.PublishingType;
} else {
this.formValues.PublishingType = this.ApplicationPublishingTypes.INTERNAL;
}
}
this.formValues.OriginalIngresses = this.ingresses;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve ingresses');
} }
} });
this.formValues.OriginalIngresses = this.filteredIngresses;
} }
refreshNamespaceData(namespace) { refreshNamespaceData(namespace) {
@ -990,13 +996,11 @@ class KubernetesCreateApplicationController {
this.state.useLoadBalancer = this.endpoint.Kubernetes.Configuration.UseLoadBalancer; this.state.useLoadBalancer = this.endpoint.Kubernetes.Configuration.UseLoadBalancer;
this.state.useServerMetrics = this.endpoint.Kubernetes.Configuration.UseServerMetrics; this.state.useServerMetrics = this.endpoint.Kubernetes.Configuration.UseServerMetrics;
const [resourcePools, nodes, ingresses, nodesLimits] = await Promise.all([ const [resourcePools, nodes, nodesLimits] = await Promise.all([
this.KubernetesResourcePoolService.get(), this.KubernetesResourcePoolService.get(),
this.KubernetesNodeService.get(), this.KubernetesNodeService.get(),
this.KubernetesIngressService.get(),
this.KubernetesNodesLimitsService.get(), this.KubernetesNodesLimitsService.get(),
]); ]);
this.ingresses = ingresses;
this.nodesLimits = nodesLimits; this.nodesLimits = nodesLimits;
this.resourcePools = _.filter(resourcePools, (resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name)); this.resourcePools = _.filter(resourcePools, (resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
@ -1023,9 +1027,9 @@ class KubernetesCreateApplicationController {
this.configurations, this.configurations,
this.persistentVolumeClaims, this.persistentVolumeClaims,
this.nodesLabels, this.nodesLabels,
this.filteredIngresses this.ingresses
); );
this.formValues.OriginalIngresses = this.filteredIngresses; this.formValues.OriginalIngresses = this.ingresses;
this.formValues.ImageModel = await this.parseImageConfiguration(this.formValues.ImageModel); this.formValues.ImageModel = await this.parseImageConfiguration(this.formValues.ImageModel);
this.savedFormValues = angular.copy(this.formValues); this.savedFormValues = angular.copy(this.formValues);
delete this.formValues.ApplicationType; delete this.formValues.ApplicationType;
@ -1042,7 +1046,6 @@ class KubernetesCreateApplicationController {
await this.refreshNamespaceData(namespace); await this.refreshNamespaceData(namespace);
} else { } else {
this.formValues.AutoScaler = KubernetesApplicationHelper.generateAutoScalerFormValueFromHorizontalPodAutoScaler(null, this.formValues.ReplicaCount); this.formValues.AutoScaler = KubernetesApplicationHelper.generateAutoScalerFormValueFromHorizontalPodAutoScaler(null, this.formValues.ReplicaCount);
this.formValues.OriginalIngressClasses = angular.copy(this.ingresses);
} }
if (this.state.isEdit) { if (this.state.isEdit) {

View File

@ -293,7 +293,7 @@ class KubernetesResourcePoolController {
this.state.ingressesLoading = true; this.state.ingressesLoading = true;
try { try {
const namespace = this.pool.Namespace.Name; const namespace = this.pool.Namespace.Name;
this.allIngresses = await this.KubernetesIngressService.get(); this.allIngresses = await this.KubernetesIngressService.get(this.state.hasWriteAuthorization ? '' : namespace);
this.ingresses = _.filter(this.allIngresses, { Namespace: namespace }); this.ingresses = _.filter(this.allIngresses, { Namespace: namespace });
_.forEach(this.ingresses, (ing) => { _.forEach(this.ingresses, (ing) => {
ing.Namespace = namespace; ing.Namespace = namespace;