SUBMARINE-1179. Add PodSecurityPolicies/SecurityContextConstraints support for RunAsAnyUser in submarine
### What is this PR for?
We need to add PodSecurityPolicies(k8s) or SecurityContextConstraints(openshift) to let pod run as a user with default user in docker container. Otherwise, pod may cause permission problems (like no permission error).
### What type of PR is it?
Bug Fix
### Todos
* [x] - Add two params in helm values.yaml: `clusterType` and `podSecurityPolicy.create`
* [x] - Change operator dockerfile to support shell params `SUBMARINE_CLUSTER_TYPE` and `SUBMARINE_POD_SECURITY_POLICY_ENABLE`
* [x] - Add PodSecurityPolicy (OpenShift has a default scc anyuid so that we need not to add)
* [x] - The processing of operator is reconstructed: create deployment run after RBAC created
* [x] - Add RunAsAnyUser policy in database\minio\server
### What is the Jira issue?
https://issues.apache.org/jira/projects/SUBMARINE/issues/SUBMARINE-1179
### How should this be tested?
<!--
* First time? Setup Travis CI as described on https://submarine.apache.org/contribution/contributions.html#continuous-integration
* Strongly recommended: add automated unit tests for any new or changed behavior
* Outline any manual steps to test the PR here.
-->
### Screenshots (if appropriate)
### Questions:
* Do the license files need updating? No
* Are there breaking changes for older versions? Yes
* Does this need new documentation? No
Author: cdmikechen <cdmikechen@hotmail.com>
Signed-off-by: Kevin Su <pingsutw@apache.org>
Closes #921 from cdmikechen/SUBMARINE-1179-new and squashes the following commits:
07a005ab [cdmikechen] Fix pod startup error when minikube supports Pod Security Policy
005f690c [cdmikechen] add submarine/finalizers in rbac
03678664 [cdmikechen] fix docker build
8fdff7d2 [cdmikechen] SUBMARINE-1179. Add PodSecurityPolicies/SecurityContextConstraints for submarine
diff --git a/.gitignore b/.gitignore
index 60e35b5..312d4aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,6 +86,8 @@
submarine-cloud-v2/vendor/*
submarine-cloud-v2/submarine-operator
submarine-cloud-v2/helm-charts/submarine-operator/charts/*
+# add operator docker build tmp
+dev-support/docker-images/operator/tmp/
# vscode file
.project
@@ -102,4 +104,4 @@
*.log
# installation binary
-submarine-serve/installation/istioctl
\ No newline at end of file
+submarine-serve/installation/istioctl
diff --git a/dev-support/docker-images/operator/Dockerfile b/dev-support/docker-images/operator/Dockerfile
index e262784..3f2795a 100644
--- a/dev-support/docker-images/operator/Dockerfile
+++ b/dev-support/docker-images/operator/Dockerfile
@@ -14,17 +14,20 @@
# limitations under the License.
FROM golang:1.16.2 AS build-image
-MAINTAINER Apache Software Foundation <dev@submarine.apache.org>
ADD tmp/submarine-cloud-v2 /usr/src
WORKDIR /usr/src
-RUN GOOS=linux go build -o submarine-operator
+# use CGO_ENABLED=0 to support alpine image
+RUN GOOS=linux CGO_ENABLED=0 go build -o submarine-operator
-FROM gcr.io/distroless/base-debian10
+# we use alpine to support shell params
+FROM alpine
+MAINTAINER Apache Software Foundation <dev@submarine.apache.org>
+
ADD tmp/submarine-cloud-v2/artifacts /usr/src/artifacts
WORKDIR /usr/src
COPY --from=build-image /usr/src/submarine-operator /usr/src/submarine-operator
-CMD ["/usr/src/submarine-operator", "-incluster=true"]
+CMD /usr/src/submarine-operator -incluster=true -clustertype=${SUBMARINE_CLUSTER_TYPE:kubernetes} -createpsp=${SUBMARINE_POD_SECURITY_POLICY_ENABLE:-true}
diff --git a/helm-charts/submarine/templates/_helpers.tpl b/helm-charts/submarine/templates/_helpers.tpl
index 8f67df5..277d3fa 100644
--- a/helm-charts/submarine/templates/_helpers.tpl
+++ b/helm-charts/submarine/templates/_helpers.tpl
@@ -31,3 +31,14 @@
{{ end }}
{{ end }}
{{ end }}
+
+{{/*
+Return the apiVersion for PodSecurityPolicy.
+*/}}
+{{- define "podSecurityPolicy.apiVersion" -}}
+{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+{{- print "policy/v1beta1" -}}
+{{- else -}}
+{{- print "extensions/v1beta1" -}}
+{{- end -}}
+{{- end -}}
diff --git a/helm-charts/submarine/templates/psp.yaml b/helm-charts/submarine/templates/psp.yaml
new file mode 100644
index 0000000..dd6cf07
--- /dev/null
+++ b/helm-charts/submarine/templates/psp.yaml
@@ -0,0 +1,40 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+{{- if .Values.podSecurityPolicy.create }}
+kind: PodSecurityPolicy
+apiVersion: {{ template "podSecurityPolicy.apiVersion" . }}
+metadata:
+ name: submarine-anyuid
+spec:
+ privileged: false
+ volumes:
+ - configMap
+ - downwardAPI
+ - emptyDir
+ - persistentVolumeClaim
+ - projected
+ - secret
+ seLinux:
+ rule: RunAsAny
+ runAsUser:
+ rule: RunAsAny
+ supplementalGroups:
+ rule: RunAsAny
+ fsGroup:
+ rule: RunAsAny
+{{- end }}
diff --git a/helm-charts/submarine/templates/pv.yaml b/helm-charts/submarine/templates/pv.yaml
index f138ed9..b03ae11 100644
--- a/helm-charts/submarine/templates/pv.yaml
+++ b/helm-charts/submarine/templates/pv.yaml
@@ -1,3 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
{{- if eq .Values.storageClass.provisioner "kubernetes.io/no-provisioner"}}
apiVersion: v1
kind: PersistentVolume
diff --git a/helm-charts/submarine/templates/rbac-kubeflow.yaml b/helm-charts/submarine/templates/rbac-kubeflow.yaml
new file mode 100644
index 0000000..00f0af3
--- /dev/null
+++ b/helm-charts/submarine/templates/rbac-kubeflow.yaml
@@ -0,0 +1,82 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+{{- if .Values.podSecurityPolicy.create }}
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: kubeflow-operator-anyuid
+rules:
+{{- if (eq "openshift" .Values.clusterType) }}
+ - apiGroups:
+ - security.openshift.io
+ resources:
+ - securitycontextconstraints
+ verbs:
+ - use
+ resourceNames:
+ - anyuid
+{{- else }}
+ - apiGroups:
+ - policy
+ resources:
+ - podsecuritypolicies
+ verbs:
+ - use
+ resourceNames:
+ - submarine-anyuid
+{{- end }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: tf-job-operator-anyuid
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: kubeflow-operator-anyuid
+subjects:
+ - kind: ServiceAccount
+ name: tf-job-operator
+ namespace: {{ .Release.Namespace }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: pytorch-operator-anyuid
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: kubeflow-operator-anyuid
+subjects:
+ - kind: ServiceAccount
+ name: pytorch-operator
+ namespace: {{ .Release.Namespace }}
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: notebook-controller-anyuid
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: kubeflow-operator-anyuid
+subjects:
+ - kind: ServiceAccount
+ name: notebook-controller-service-account
+ namespace: {{ .Release.Namespace }}
+{{- end }}
diff --git a/helm-charts/submarine/templates/rbac.yaml b/helm-charts/submarine/templates/rbac.yaml
index 02dec67..2c854ef 100644
--- a/helm-charts/submarine/templates/rbac.yaml
+++ b/helm-charts/submarine/templates/rbac.yaml
@@ -25,6 +25,7 @@
resources:
- submarines
- submarines/status
+ - submarines/finalizers
verbs:
- "*"
- apiGroups:
@@ -90,6 +91,27 @@
- customresourcedefinitions/status
verbs:
- "*"
+{{- if .Values.podSecurityPolicy.create }}
+{{- if (eq "openshift" .Values.clusterType) }}
+ - apiGroups:
+ - security.openshift.io
+ resources:
+ - securitycontextconstraints
+ verbs:
+ - use
+ resourceNames:
+ - anyuid
+{{- else }}
+ - apiGroups:
+ - policy
+ resources:
+ - podsecuritypolicies
+ verbs:
+ - use
+ resourceNames:
+ - submarine-anyuid
+{{- end }}
+{{- end }}
---
apiVersion: v1
kind: ServiceAccount
diff --git a/helm-charts/submarine/templates/submarine-operator.yaml b/helm-charts/submarine/templates/submarine-operator.yaml
index 3c7b587..5a7b2be 100644
--- a/helm-charts/submarine/templates/submarine-operator.yaml
+++ b/helm-charts/submarine/templates/submarine-operator.yaml
@@ -42,6 +42,11 @@
name: "{{ .Values.name }}"
resources: {}
imagePullPolicy: IfNotPresent
+ env:
+ - name: SUBMARINE_POD_SECURITY_POLICY_ENABLE
+ value: "{{ .Values.podSecurityPolicy.create }}"
+ - name: SUBMARINE_CLUSTER_TYPE
+ value: {{ .Values.clusterType }}
serviceAccountName: submarine-operator
status: {}
{{ end }}
diff --git a/helm-charts/submarine/values.yaml b/helm-charts/submarine/values.yaml
index 534d208..8366e5c 100644
--- a/helm-charts/submarine/values.yaml
+++ b/helm-charts/submarine/values.yaml
@@ -30,3 +30,13 @@
provisioner: k8s.io/minikube-hostpath
# parameters describe volumes belonging to the storage class
parameters:
+
+# k8s cluster type. can be: kubernetes or openshift
+clusterType: kubernetes
+
+# PodSecurityPolicy configuration
+# ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/
+podSecurityPolicy:
+ # Specifies whether a PodSecurityPolicy should be created,
+ # This configuration enables the database/minio/server to set securityContext.runAsUser
+ create: true
diff --git a/submarine-cloud-v2/README.md b/submarine-cloud-v2/README.md
index 7dbfe86..4e4ff17 100644
--- a/submarine-cloud-v2/README.md
+++ b/submarine-cloud-v2/README.md
@@ -32,6 +32,8 @@
go mod vendor
# Run the cluster
minikube start --vm-driver=docker --cpus 8 --memory 4096 --kubernetes-version v1.21.2
+# Or if you want to support Pod Security Policy (https://minikube.sigs.k8s.io/docs/tutorials/using_psp), you can use the following command to start cluster
+minikube start --extra-config=apiserver.enable-admission-plugins=PodSecurityPolicy --addons=pod-security-policy --vm-driver=docker --cpus 8 --memory 4096 --kubernetes-version v1.21.2
```
## Set up storage class fields
diff --git a/submarine-cloud-v2/artifacts/submarine/submarine-database.yaml b/submarine-cloud-v2/artifacts/submarine/submarine-database.yaml
index a6efafe..b368e93 100644
--- a/submarine-cloud-v2/artifacts/submarine/submarine-database.yaml
+++ b/submarine-cloud-v2/artifacts/submarine/submarine-database.yaml
@@ -60,6 +60,7 @@
labels:
app: "submarine-database"
spec:
+ serviceAccountName: "submarine-storage"
containers:
- name: "submarine-database"
image: "apache/submarine:database-0.7.0"
diff --git a/submarine-cloud-v2/artifacts/submarine/submarine-minio.yaml b/submarine-cloud-v2/artifacts/submarine/submarine-minio.yaml
index a764106..799fc09 100644
--- a/submarine-cloud-v2/artifacts/submarine/submarine-minio.yaml
+++ b/submarine-cloud-v2/artifacts/submarine/submarine-minio.yaml
@@ -57,6 +57,7 @@
labels:
app: submarine-minio
spec:
+ serviceAccountName: "submarine-storage"
containers:
- name: submarine-minio-container
image: minio/minio:RELEASE.2021-02-14T04-01-33Z
diff --git a/submarine-cloud-v2/artifacts/submarine/submarine-mlflow.yaml b/submarine-cloud-v2/artifacts/submarine/submarine-mlflow.yaml
index dbc513a..dfe05ee 100644
--- a/submarine-cloud-v2/artifacts/submarine/submarine-mlflow.yaml
+++ b/submarine-cloud-v2/artifacts/submarine/submarine-mlflow.yaml
@@ -58,6 +58,7 @@
labels:
app: submarine-mlflow
spec:
+ serviceAccountName: "submarine-storage"
initContainers:
- name: check-database-connection
image: busybox:1.28
diff --git a/submarine-cloud-v2/artifacts/submarine/submarine-storage-rbac.yaml b/submarine-cloud-v2/artifacts/submarine/submarine-storage-rbac.yaml
new file mode 100644
index 0000000..9d4e113
--- /dev/null
+++ b/submarine-cloud-v2/artifacts/submarine/submarine-storage-rbac.yaml
@@ -0,0 +1,41 @@
+---
+# Source: submarine/templates/rbac.yaml
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: "submarine-storage"
+rules: []
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: submarine-storage
+---
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: "submarine-storage"
+subjects:
+ - kind: ServiceAccount
+ name: "submarine-storage"
+roleRef:
+ kind: Role
+ name: "submarine-storage"
+ apiGroup: rbac.authorization.k8s.io
diff --git a/submarine-cloud-v2/artifacts/submarine/submarine-tensorboard.yaml b/submarine-cloud-v2/artifacts/submarine/submarine-tensorboard.yaml
index ae60e57..b49776b 100644
--- a/submarine-cloud-v2/artifacts/submarine/submarine-tensorboard.yaml
+++ b/submarine-cloud-v2/artifacts/submarine/submarine-tensorboard.yaml
@@ -56,6 +56,7 @@
labels:
app: submarine-tensorboard
spec:
+ serviceAccountName: "submarine-storage"
containers:
- name: submarine-tensorboard-container
image: tensorflow/tensorflow:1.11.0
diff --git a/submarine-cloud-v2/main.go b/submarine-cloud-v2/main.go
index 607a12f..486dda5 100644
--- a/submarine-cloud-v2/main.go
+++ b/submarine-cloud-v2/main.go
@@ -38,9 +38,11 @@
)
var (
- masterURL string
- kubeconfig string
- incluster bool
+ masterURL string
+ kubeconfig string
+ incluster bool
+ clusterType string
+ createPodSecurityPolicy bool
)
func initKubeConfig() (*rest.Config, error) {
@@ -88,6 +90,8 @@
// Create a Submarine operator
submarineController := NewSubmarineController(
incluster,
+ clusterType,
+ createPodSecurityPolicy,
kubeClient,
submarineClient,
traefikClient,
@@ -110,6 +114,8 @@
func NewSubmarineController(
incluster bool,
+ clusterType string,
+ createPodSecurityPolicy bool,
kubeClient *kubernetes.Clientset,
submarineClient *clientset.Clientset,
traefikClient *traefikclientset.Clientset,
@@ -120,6 +126,8 @@
bc := controller.NewControllerBuilderConfig()
bc.
InCluster(incluster).
+ WithClusterType(clusterType).
+ WithCreatePodSecurityPolicy(createPodSecurityPolicy).
WithKubeClientset(kubeClient).
WithSubmarineClientset(submarineClient).
WithTraefikClientset(traefikClient).
@@ -143,4 +151,6 @@
flag.BoolVar(&incluster, "incluster", false, "Run submarine-operator in-cluster")
flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("HOME")+"/.kube/config", "Path to a kubeconfig. Only required if out-of-cluster.")
flag.StringVar(&masterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
+ flag.StringVar(&clusterType, "clustertype", "kubernetes", "K8s cluster type, can be kubernetes or openshift")
+ flag.BoolVar(&createPodSecurityPolicy, "createpsp", true, "Specifies whether a PodSecurityPolicy should be created. This configuration enables the database/minio/server to set securityContext.runAsUser")
}
diff --git a/submarine-cloud-v2/pkg/controller/controller.go b/submarine-cloud-v2/pkg/controller/controller.go
index bbb965e..cf7bd03 100644
--- a/submarine-cloud-v2/pkg/controller/controller.go
+++ b/submarine-cloud-v2/pkg/controller/controller.go
@@ -29,6 +29,7 @@
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
+ rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -60,6 +61,7 @@
tensorboardName = "submarine-tensorboard"
mlflowName = "submarine-mlflow"
minioName = "submarine-minio"
+ storageName = "submarine-storage"
ingressName = serverName + "-ingress"
databasePvcName = databaseName + "-pvc"
tensorboardPvcName = tensorboardName + "-pvc"
@@ -80,6 +82,7 @@
tensorboardYamlPath = artifactPath + "submarine-tensorboard.yaml"
rbacYamlPath = artifactPath + "submarine-rbac.yaml"
observerRbacYamlPath = artifactPath + "submarine-observer-rbac.yaml"
+ storageRbacYamlPath = artifactPath + "submarine-storage-rbac.yaml"
)
var dependents = []string{serverName, tensorboardName, mlflowName, minioName}
@@ -99,6 +102,22 @@
MessageResourceSynced = "Submarine synced successfully"
)
+// Default k8s anyuid role rule
+var k8sAnyuidRoleRule = rbacv1.PolicyRule{
+ APIGroups: []string{"policy"},
+ Verbs: []string{"use"},
+ Resources: []string{"podsecuritypolicies"},
+ ResourceNames: []string{"submarine-anyuid"},
+}
+
+// Openshift anyuid role rule
+var openshiftAnyuidRoleRule = rbacv1.PolicyRule{
+ APIGroups: []string{"security.openshift.io"},
+ Verbs: []string{"use"},
+ Resources: []string{"securitycontextconstraints"},
+ ResourceNames: []string{"anyuid"},
+}
+
// Controller is the controller implementation for Submarine resources
type Controller struct {
// kubeclientset is a standard kubernetes clientset
@@ -130,7 +149,9 @@
// Kubernetes API.
recorder record.EventRecorder
- incluster bool
+ incluster bool
+ clusterType string
+ createPodSecurityPolicy bool
}
func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
@@ -437,6 +458,22 @@
func (c *Controller) createSubmarine(submarine *v1alpha1.Submarine) error {
var err error
+ // We create rbac first, this ensures that any dependency based on it will not go wrong
+ err = c.createSubmarineServerRBAC(submarine)
+ if err != nil && !errors.IsAlreadyExists(err) {
+ return err
+ }
+
+ err = c.createSubmarineStorageRBAC(submarine)
+ if err != nil && !errors.IsAlreadyExists(err) {
+ return err
+ }
+
+ err = c.createSubmarineObserverRBAC(submarine)
+ if err != nil && !errors.IsAlreadyExists(err) {
+ return err
+ }
+
err = c.createSubmarineServer(submarine)
if err != nil && !errors.IsAlreadyExists(err) {
return err
@@ -452,16 +489,6 @@
return err
}
- err = c.createSubmarineServerRBAC(submarine)
- if err != nil && !errors.IsAlreadyExists(err) {
- return err
- }
-
- err = c.createSubmarineObserverRBAC(submarine)
- if err != nil && !errors.IsAlreadyExists(err) {
- return err
- }
-
err = c.createSubmarineTensorboard(submarine)
if err != nil && !errors.IsAlreadyExists(err) {
return err
diff --git a/submarine-cloud-v2/pkg/controller/controller_builder.go b/submarine-cloud-v2/pkg/controller/controller_builder.go
index 8d3ab63..17b8fca 100644
--- a/submarine-cloud-v2/pkg/controller/controller_builder.go
+++ b/submarine-cloud-v2/pkg/controller/controller_builder.go
@@ -63,6 +63,8 @@
workqueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Submarines")
cb.controller.incluster = cb.config.incluster
+ cb.controller.clusterType = cb.config.clusterType
+ cb.controller.createPodSecurityPolicy = cb.config.createPodSecurityPolicy
cb.controller.recorder = recorder
cb.controller.workqueue = workqueue
diff --git a/submarine-cloud-v2/pkg/controller/controller_builder_config.go b/submarine-cloud-v2/pkg/controller/controller_builder_config.go
index 2212fb5..337616b 100644
--- a/submarine-cloud-v2/pkg/controller/controller_builder_config.go
+++ b/submarine-cloud-v2/pkg/controller/controller_builder_config.go
@@ -31,6 +31,8 @@
type BuilderConfig struct {
incluster bool
+ clusterType string
+ createPodSecurityPolicy bool
kubeclientset kubernetes.Interface
submarineclientset clientset.Interface
traefikclientset traefik.Interface
@@ -58,6 +60,20 @@
return bc
}
+func (bc *BuilderConfig) WithClusterType(
+ clusterType string,
+) *BuilderConfig {
+ bc.clusterType = clusterType
+ return bc
+}
+
+func (bc *BuilderConfig) WithCreatePodSecurityPolicy(
+ createPodSecurityPolicy bool,
+) *BuilderConfig {
+ bc.createPodSecurityPolicy = createPodSecurityPolicy
+ return bc
+}
+
func (bc *BuilderConfig) WithKubeClientset(
kubeclientset kubernetes.Interface,
) *BuilderConfig {
diff --git a/submarine-cloud-v2/pkg/controller/submarine_observer_rbac.go b/submarine-cloud-v2/pkg/controller/submarine_observer_rbac.go
index 5f65119..b845b24 100644
--- a/submarine-cloud-v2/pkg/controller/submarine_observer_rbac.go
+++ b/submarine-cloud-v2/pkg/controller/submarine_observer_rbac.go
@@ -57,7 +57,7 @@
// createSubmarineObserverRBAC is a function to create RBAC for submarine-observer which will be binded on service account: default.
// Reference: https://github.com/apache/submarine/blob/master/helm-charts/submarine/templates/rbac.yaml
func (c *Controller) createSubmarineObserverRBAC(submarine *v1alpha1.Submarine) error {
- klog.Info("[createSubmarineServerRBAC]")
+ klog.Info("[createSubmarineObserverRBAC]")
// Step1: Create Role
role, err := c.roleLister.Roles(submarine.Namespace).Get(observerName)
diff --git a/submarine-cloud-v2/pkg/controller/submarine_server.go b/submarine-cloud-v2/pkg/controller/submarine_server.go
index e71ee0b..a0370ea 100644
--- a/submarine-cloud-v2/pkg/controller/submarine_server.go
+++ b/submarine-cloud-v2/pkg/controller/submarine_server.go
@@ -30,19 +30,6 @@
"k8s.io/klog/v2"
)
-func newSubmarineServerServiceAccount(submarine *v1alpha1.Submarine) *corev1.ServiceAccount {
- serviceAccount, err := ParseServiceAccountYaml(serverYamlPath)
- if err != nil {
- klog.Info("[Error] ParseServiceAccountYaml", err)
- }
-
- serviceAccount.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
- *metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
- }
-
- return serviceAccount
-}
-
func newSubmarineServerService(submarine *v1alpha1.Submarine) *corev1.Service {
service, err := ParseServiceYaml(serverYamlPath)
if err != nil {
@@ -111,28 +98,7 @@
func (c *Controller) createSubmarineServer(submarine *v1alpha1.Submarine) error {
klog.Info("[createSubmarineServer]")
- // Step1: Create ServiceAccount
- serviceaccount, err := c.serviceaccountLister.ServiceAccounts(submarine.Namespace).Get(serverName)
- // If the resource doesn't exist, we'll create it
- if errors.IsNotFound(err) {
- serviceaccount, err = c.kubeclientset.CoreV1().ServiceAccounts(submarine.Namespace).Create(context.TODO(), newSubmarineServerServiceAccount(submarine), metav1.CreateOptions{})
- klog.Info(" Create ServiceAccount: ", serviceaccount.Name)
- }
-
- // If an error occurs during Get/Create, we'll requeue the item so we can
- // attempt processing again later. This could have been caused by a
- // temporary network failure, or any other transient reason.
- if err != nil {
- return err
- }
-
- if !metav1.IsControlledBy(serviceaccount, submarine) {
- msg := fmt.Sprintf(MessageResourceExists, serviceaccount.Name)
- c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
- return fmt.Errorf(msg)
- }
-
- // Step2: Create Service
+ // Step1: Create Service
service, err := c.serviceLister.Services(submarine.Namespace).Get(serverName)
// If the resource doesn't exist, we'll create it
if errors.IsNotFound(err) {
@@ -153,7 +119,7 @@
return fmt.Errorf(msg)
}
- // Step3: Create Deployment
+ // Step2: Create Deployment
deployment, err := c.deploymentLister.Deployments(submarine.Namespace).Get(serverName)
// If the resource doesn't exist, we'll create it
if errors.IsNotFound(err) {
diff --git a/submarine-cloud-v2/pkg/controller/submarine_server_rbac.go b/submarine-cloud-v2/pkg/controller/submarine_server_rbac.go
index 61e0bb8..12268a2 100644
--- a/submarine-cloud-v2/pkg/controller/submarine_server_rbac.go
+++ b/submarine-cloud-v2/pkg/controller/submarine_server_rbac.go
@@ -30,7 +30,20 @@
"k8s.io/klog/v2"
)
-func newSubmarineServerRole(submarine *v1alpha1.Submarine) *rbacv1.Role {
+func newSubmarineServerServiceAccount(submarine *v1alpha1.Submarine) *corev1.ServiceAccount {
+ serviceAccount, err := ParseServiceAccountYaml(serverYamlPath)
+ if err != nil {
+ klog.Info("[Error] ParseServiceAccountYaml", err)
+ }
+
+ serviceAccount.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+ *metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+ }
+
+ return serviceAccount
+}
+
+func newSubmarineServerRole(c *Controller, submarine *v1alpha1.Submarine) *rbacv1.Role {
role, err := ParseRoleYaml(rbacYamlPath)
if err != nil {
klog.Info("[Error] ParseRole", err)
@@ -39,6 +52,15 @@
*metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
}
+ if c.createPodSecurityPolicy {
+ // If cluster type is openshift and need create pod security policy, we need add anyuid scc, or we add k8s psp
+ if c.clusterType == "openshift" {
+ role.Rules = append(role.Rules, openshiftAnyuidRoleRule)
+ } else {
+ role.Rules = append(role.Rules, k8sAnyuidRoleRule)
+ }
+ }
+
return role
}
@@ -59,11 +81,32 @@
func (c *Controller) createSubmarineServerRBAC(submarine *v1alpha1.Submarine) error {
klog.Info("[createSubmarineServerRBAC]")
- // Step1: Create Role
+ // Step1: Create ServiceAccount
+ serviceaccount, err := c.serviceaccountLister.ServiceAccounts(submarine.Namespace).Get(serverName)
+ // If the resource doesn't exist, we'll create it
+ if errors.IsNotFound(err) {
+ serviceaccount, err = c.kubeclientset.CoreV1().ServiceAccounts(submarine.Namespace).Create(context.TODO(), newSubmarineServerServiceAccount(submarine), metav1.CreateOptions{})
+ klog.Info(" Create ServiceAccount: ", serviceaccount.Name)
+ }
+
+ // If an error occurs during Get/Create, we'll requeue the item so we can
+ // attempt processing again later. This could have been caused by a
+ // temporary network failure, or any other transient reason.
+ if err != nil {
+ return err
+ }
+
+ if !metav1.IsControlledBy(serviceaccount, submarine) {
+ msg := fmt.Sprintf(MessageResourceExists, serviceaccount.Name)
+ c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
+ return fmt.Errorf(msg)
+ }
+
+ // Step2: Create Role
role, err := c.roleLister.Roles(submarine.Namespace).Get(serverName)
// If the resource doesn't exist, we'll create it
if errors.IsNotFound(err) {
- role, err = c.kubeclientset.RbacV1().Roles(submarine.Namespace).Create(context.TODO(), newSubmarineServerRole(submarine), metav1.CreateOptions{})
+ role, err = c.kubeclientset.RbacV1().Roles(submarine.Namespace).Create(context.TODO(), newSubmarineServerRole(c, submarine), metav1.CreateOptions{})
klog.Info(" Create Role: ", role.Name)
}
@@ -80,6 +123,7 @@
return fmt.Errorf(msg)
}
+ // Step3: Create Role Binding
rolebinding, rolebinding_err := c.rolebindingLister.RoleBindings(submarine.Namespace).Get(serverName)
// If the resource doesn't exist, we'll create it
if errors.IsNotFound(rolebinding_err) {
diff --git a/submarine-cloud-v2/pkg/controller/submarine_storage_rbac.go b/submarine-cloud-v2/pkg/controller/submarine_storage_rbac.go
new file mode 100644
index 0000000..05a38ad
--- /dev/null
+++ b/submarine-cloud-v2/pkg/controller/submarine_storage_rbac.go
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package controller
+
+import (
+ "context"
+ "fmt"
+ v1alpha1 "github.com/apache/submarine/submarine-cloud-v2/pkg/apis/submarine/v1alpha1"
+
+ corev1 "k8s.io/api/core/v1"
+ rbacv1 "k8s.io/api/rbac/v1"
+ "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/klog/v2"
+)
+
+func newSubmarineStorageRole(c *Controller, submarine *v1alpha1.Submarine) *rbacv1.Role {
+ role, err := ParseRoleYaml(storageRbacYamlPath)
+ if err != nil {
+ klog.Info("[Error] ParseRole", err)
+ }
+ role.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+ *metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+ }
+
+ // If cluster type is openshift and need create pod security policy, we need add anyuid scc, or we add k8s psp
+ if c.clusterType == "openshift" {
+ role.Rules = append(role.Rules, openshiftAnyuidRoleRule)
+ } else {
+ role.Rules = append(role.Rules, k8sAnyuidRoleRule)
+ }
+
+ return role
+}
+
+func newSubmarineStorageRoleBinding(submarine *v1alpha1.Submarine) *rbacv1.RoleBinding {
+ roleBinding, err := ParseRoleBindingYaml(storageRbacYamlPath)
+ if err != nil {
+ klog.Info("[Error] ParseRoleBinding", err)
+ }
+ roleBinding.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+ *metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+ }
+
+ return roleBinding
+}
+
+func newSubmarineStorageServiceAccount(submarine *v1alpha1.Submarine) *corev1.ServiceAccount {
+ serviceAccount, err := ParseServiceAccountYaml(storageRbacYamlPath)
+ if err != nil {
+ klog.Info("[Error] ParseServiceAccountYaml", err)
+ }
+
+ serviceAccount.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
+ *metav1.NewControllerRef(submarine, v1alpha1.SchemeGroupVersion.WithKind("Submarine")),
+ }
+
+ return serviceAccount
+}
+
+// createSubmarineStorageRBAC is a function to create RBAC for submarine-database and submarine-minio which will be binded on service account: submarine-storage.
+// Reference: https://github.com/apache/submarine/blob/master/submarine-cloud-v2/artifacts/submarine/submarine-storage-rbac.yaml
+func (c *Controller) createSubmarineStorageRBAC(submarine *v1alpha1.Submarine) error {
+ klog.Info("[createSubmarineStorageRBAC]")
+
+ // Step1: Create ServiceAccount
+ serviceaccount, err := c.serviceaccountLister.ServiceAccounts(submarine.Namespace).Get(storageName)
+ // If the resource doesn't exist, we'll create it
+ if errors.IsNotFound(err) {
+ serviceaccount, err = c.kubeclientset.CoreV1().ServiceAccounts(submarine.Namespace).Create(context.TODO(), newSubmarineStorageServiceAccount(submarine), metav1.CreateOptions{})
+ klog.Info(" Create ServiceAccount: ", serviceaccount.Name)
+ }
+
+ // Step2: Pod Security Policy if needed
+ if c.createPodSecurityPolicy {
+ // Step2.1: Create Role
+ role, err := c.roleLister.Roles(submarine.Namespace).Get(storageName)
+ // If the resource doesn't exist, we'll create it
+ if errors.IsNotFound(err) {
+ role, err = c.kubeclientset.RbacV1().Roles(submarine.Namespace).Create(context.TODO(), newSubmarineStorageRole(c, submarine), metav1.CreateOptions{})
+ klog.Info(" Create Role: ", role.Name)
+ }
+
+ // If an error occurs during Get/Create, we'll requeue the item so we can
+ // attempt processing again later. This could have been caused by a
+ // temporary network failure, or any other transient reason.
+ if err != nil {
+ return err
+ }
+
+ if !metav1.IsControlledBy(role, submarine) {
+ msg := fmt.Sprintf(MessageResourceExists, role.Name)
+ c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
+ return fmt.Errorf(msg)
+ }
+
+ // Step2.2: Create Role Binding
+ rolebinding, rolebinding_err := c.rolebindingLister.RoleBindings(submarine.Namespace).Get(storageName)
+ // If the resource doesn't exist, we'll create it
+ if errors.IsNotFound(rolebinding_err) {
+ rolebinding, rolebinding_err = c.kubeclientset.RbacV1().RoleBindings(submarine.Namespace).Create(context.TODO(), newSubmarineStorageRoleBinding(submarine), metav1.CreateOptions{})
+ klog.Info(" Create RoleBinding: ", rolebinding.Name)
+ }
+
+ // If an error occurs during Get/Create, we'll requeue the item so we can
+ // attempt processing again later. This could have been caused by a
+ // temporary network failure, or any other transient reason.
+ if rolebinding_err != nil {
+ return rolebinding_err
+ }
+
+ if !metav1.IsControlledBy(rolebinding, submarine) {
+ msg := fmt.Sprintf(MessageResourceExists, rolebinding.Name)
+ c.recorder.Event(submarine, corev1.EventTypeWarning, ErrResourceExists, msg)
+ return fmt.Errorf(msg)
+ }
+ }
+
+ return nil
+}