Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate TLS secrets generation for Kubernetes family infrastructures #220

Merged
merged 15 commits into from
Apr 28, 2020
2 changes: 2 additions & 0 deletions deploy/operator-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ spec:
value: quay.io/eclipse/che-plugin-registry:7.12.0
- name: IMAGE_default_devfile_registry
value: quay.io/eclipse/che-devfile-registry:7.12.0
- name: IMAGE_default_che_tls_secrets_creation_job
value: quay.io/eclipse/che-tls-secret-creator:alpine-3029769
- name: IMAGE_default_pvc_jobs
value: registry.access.redhat.com/ubi8-minimal:8.1-409
- name: IMAGE_default_postgres
Expand Down
2 changes: 2 additions & 0 deletions deploy/operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ spec:
value: quay.io/eclipse/che-plugin-registry:7.12.0
- name: IMAGE_default_devfile_registry
value: quay.io/eclipse/che-devfile-registry:7.12.0
- name: IMAGE_default_che_tls_secrets_creation_job
value: quay.io/eclipse/che-tls-secret-creator:alpine-3029769
- name: IMAGE_default_pvc_jobs
value: registry.access.redhat.com/ubi8-minimal:8.1-409
- name: IMAGE_default_postgres
Expand Down
6 changes: 6 additions & 0 deletions deploy/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ rules:
- ingresses
verbs:
- '*'
- apiGroups:
- batch
resources:
- jobs
verbs:
- '*'
- apiGroups:
- route.openshift.io
resources:
Expand Down
130 changes: 98 additions & 32 deletions pkg/controller/che/che_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,26 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
}

// Handle Che TLS certificates on Kubernetes like infrastructures
if instance.Spec.Server.TlsSupport && instance.Spec.Server.SelfSignedCert && !isOpenShift {
// Ensure TLS configuration is correct
if err := CheckAndUpdateTLSConfiguration(instance, clusterAPI); err != nil {
instance, _ = r.GetCR(request)
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err
}

// Create TLS secrets if needed
result, err := HandleCheTLSSecrets(instance, clusterAPI)
if result.Requeue || result.RequeueAfter > 0 {
if err != nil {
logrus.Error(err)
}
if !tests {
return result, err
}
}
}

// Get custom ConfigMap
// if it exists, add the data into CustomCheProperties
customConfigMap := &corev1.ConfigMap{}
Expand Down Expand Up @@ -401,44 +421,97 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
// create service accounts:
// che is the one which token is used to create workspace objects
// che-workspace is SA used by plugins like exec and terminal with limited privileges
cheServiceAccount := deploy.NewServiceAccount(instance, "che")
if err := r.CreateServiceAccount(instance, cheServiceAccount); err != nil {
return reconcile.Result{}, err
cheSA, err := deploy.SyncServiceAccountToCluster(instance, "che", clusterAPI)
if cheSA == nil {
logrus.Info("Waiting on service account 'che' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
workspaceServiceAccount := deploy.NewServiceAccount(instance, "che-workspace")
if err := r.CreateServiceAccount(instance, workspaceServiceAccount); err != nil {
return reconcile.Result{}, err

cheWorkspaceSA, err := deploy.SyncServiceAccountToCluster(instance, "che-workspace", clusterAPI)
if cheWorkspaceSA == nil {
logrus.Info("Waiting on service account 'che-workspace' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}

// create exec and view roles for CheCluster server and workspaces
execRole := deploy.NewRole(instance, "exec", []string{"pods/exec"}, []string{"*"})
if err := r.CreateNewRole(instance, execRole); err != nil {
return reconcile.Result{}, err
role, err := deploy.SyncRoleToCluster(instance, "exec", []string{"pods/exec"}, []string{"*"}, clusterAPI)
if role == nil {
logrus.Info("Waiting on role 'exec' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
viewRole := deploy.NewRole(instance, "view", []string{"pods"}, []string{"list"})
if err := r.CreateNewRole(instance, viewRole); err != nil {
return reconcile.Result{}, err

viewRole, err := deploy.SyncRoleToCluster(instance, "view", []string{"pods"}, []string{"list"}, clusterAPI)
if viewRole == nil {
logrus.Info("Waiting on role 'view' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
// create RoleBindings for created (and existing ClusterRole) roles and service accounts
cheRoleBinding := deploy.NewRoleBinding(instance, "che", cheServiceAccount.Name, "edit", "ClusterRole")
if err := r.CreateNewRoleBinding(instance, cheRoleBinding); err != nil {
return reconcile.Result{}, err

cheRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che", "che", "edit", "ClusterRole", clusterAPI)
if cheRoleBinding == nil {
logrus.Info("Waiting on role binding 'che' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
execRoleBinding := deploy.NewRoleBinding(instance, "che-workspace-exec", workspaceServiceAccount.Name, execRole.Name, "Role")
if err = r.CreateNewRoleBinding(instance, execRoleBinding); err != nil {
return reconcile.Result{}, err

cheWSExecRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che-workspace-exec", "che-workspace", "exec", "Role", clusterAPI)
if cheWSExecRoleBinding == nil {
logrus.Info("Waiting on role binding 'che-workspace-exec' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
viewRoleBinding := deploy.NewRoleBinding(instance, "che-workspace-view", workspaceServiceAccount.Name, viewRole.Name, "Role")
if err := r.CreateNewRoleBinding(instance, viewRoleBinding); err != nil {
return reconcile.Result{}, err

cheWSViewRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che-workspace-view", "che-workspace", "view", "Role", clusterAPI)
if cheWSViewRoleBinding == nil {
logrus.Info("Waiting on role binding 'che-workspace-view' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}

// If the user specified an additional cluster role to use for the Che workspace, create a role binding for it
// Use a role binding instead of a cluster role binding to keep the additional access scoped to the workspace's namespace
workspaceClusterRole := instance.Spec.Server.CheWorkspaceClusterRole
if workspaceClusterRole != "" {
customRoleBinding := deploy.NewRoleBinding(instance, "che-workspace-custom", workspaceServiceAccount.Name, workspaceClusterRole, "ClusterRole")
if err = r.CreateNewRoleBinding(instance, customRoleBinding); err != nil {
return reconcile.Result{}, err
cheWSCustomRoleBinding, err := deploy.SyncRoleBindingToCluster(instance, "che-workspace-custom", "view", workspaceClusterRole, "ClusterRole", clusterAPI)
if cheWSCustomRoleBinding == nil {
logrus.Info("Waiting on role binding 'che-workspace-custom' to be created")
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
}

Expand Down Expand Up @@ -782,10 +855,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
}
guessedDevfileRegistryURL := protocol + "://" + host

if err != nil {
return reconcile.Result{}, err
}
if devfileRegistryURL == "" {
devfileRegistryURL = guessedDevfileRegistryURL
}
Expand Down Expand Up @@ -942,9 +1011,6 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}

guessedPluginRegistryURL := protocol + "://" + host
if err != nil {
return reconcile.Result{}, err
}
guessedPluginRegistryURL += "/v3"
if pluginRegistryURL == "" {
pluginRegistryURL = guessedPluginRegistryURL
Expand Down
63 changes: 0 additions & 63 deletions pkg/controller/che/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,54 +21,12 @@ import (
oauth "github.com/openshift/api/oauth/v1"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func (r *ReconcileChe) CreateServiceAccount(cr *orgv1.CheCluster, serviceAccount *corev1.ServiceAccount) error {
if err := controllerutil.SetControllerReference(cr, serviceAccount, r.scheme); err != nil {
return err
}
serviceAccountFound := &corev1.ServiceAccount{}
err := r.client.Get(context.TODO(), types.NamespacedName{Name: serviceAccount.Name, Namespace: serviceAccount.Namespace}, serviceAccountFound)
if err != nil && errors.IsNotFound(err) {
logrus.Infof("Creating a new object: %s, name: %s", serviceAccount.Kind, serviceAccount.Name)
err = r.client.Create(context.TODO(), serviceAccount)
if err != nil {
logrus.Errorf("Failed to create %s %s: %s", serviceAccount.Name, serviceAccount.Kind, err)
return err
}
return nil
} else if err != nil {
return err
}
return nil
}

func (r *ReconcileChe) CreateNewRole(instance *orgv1.CheCluster, role *rbac.Role) error {
if err := controllerutil.SetControllerReference(instance, role, r.scheme); err != nil {
return err
}
roleFound := &rbac.Role{}
err := r.client.Get(context.TODO(), types.NamespacedName{Name: role.Name, Namespace: role.Namespace}, roleFound)
if err != nil && errors.IsNotFound(err) {
logrus.Infof("Creating a new object: %s, name: %s", role.Kind, role.Name)
err = r.client.Create(context.TODO(), role)
if err != nil {
logrus.Errorf("Failed to create %s %s: %s", role.Name, role.Kind, err)
return err
}
return nil
} else if err != nil {
logrus.Errorf("An error occurred: %s", err)
return err
}
return nil
}

func (r *ReconcileChe) CreateNewSecret(instance *orgv1.CheCluster, secret *corev1.Secret) error {
if err := controllerutil.SetControllerReference(instance, secret, r.scheme); err != nil {
logrus.Errorf("An error occurred: %s", err)
Expand Down Expand Up @@ -111,27 +69,6 @@ func (r *ReconcileChe) CreateNewOauthClient(instance *orgv1.CheCluster, oAuthCli
return nil
}

func (r *ReconcileChe) CreateNewRoleBinding(instance *orgv1.CheCluster, roleBinding *rbac.RoleBinding) error {
if err := controllerutil.SetControllerReference(instance, roleBinding, r.scheme); err != nil {
return err
}
roleBindingFound := &rbac.RoleBinding{}
err := r.client.Get(context.TODO(), types.NamespacedName{Name: roleBinding.Name, Namespace: roleBinding.Namespace}, roleBindingFound)
if err != nil && errors.IsNotFound(err) {
logrus.Infof("Creating a new object: %s, name: %s", roleBinding.Kind, roleBinding.Name)
err = r.client.Create(context.TODO(), roleBinding)
if err != nil {
logrus.Errorf("Failed to create %s %s: %s", roleBinding.Name, roleBinding.Kind, err)
return err
}
return nil
} else if err != nil {
logrus.Errorf("An error occurred: %s", err)
return err
}
return nil
}

func (r *ReconcileChe) CreateIdentityProviderItems(instance *orgv1.CheCluster, request reconcile.Request, cheFlavor string, keycloakDeploymentName string, isOpenShift4 bool) (err error) {
tests := r.tests
oAuthClientName := instance.Spec.Auth.OAuthClientName
Expand Down
Loading