feat: native user authentication (#861)

Signed-off-by: Michal Vala <mvala@redhat.com>
Co-authored-by: Anatolii Bazko <abazko@redhat.com>
pull/889/head
Michal Vala 2021-07-02 16:11:58 +02:00 committed by GitHub
parent e71703c7f7
commit b9ccdb73d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 807 additions and 190 deletions

View File

@ -636,31 +636,13 @@ rules:
- update
- watch
- apiGroups:
- che.eclipse.org
- org.eclipse.che
resources:
- '*'
- checlusters
- checlusters/status
- checlusters/finalizers
verbs:
- '*'
- apiGroups:
- che.eclipse.org
resources:
- chemanagers
verbs:
- '*'
- apiGroups:
- che.eclipse.org
resources:
- chemanagers/status
verbs:
- get
- patch
- update
- apiGroups:
- che.eclipse.org
resources:
- chemanagers/finalizers
verbs:
- update
- apiGroups:
- controller.devfile.io
resources:

View File

@ -61,6 +61,20 @@ spec:
details about the external identity provider you are about to
use. See also all the other fields starting with: `identityProvider`.'
type: boolean
gatewayAuthenticationSidecarImage:
description: Gateway sidecar responsible for authentication when
NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy]
or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy].
type: string
gatewayAuthorizationSidecarImage:
description: Gateway sidecar responsible for authorization when
NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy]
or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy]
type: string
gatewayHeaderRewriteSidecarImage:
description: Header Rewrite Proxy sidecar image is used to properly
set authorization header. See link:https://github.com/che-incubator/header-rewrite-proxy[header-rewrite-proxy]
type: string
identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator
user. Defaults to `admin`.
@ -208,6 +222,11 @@ spec:
'openshift-config' namespace by Operator. Note that this solution
is Openshift 4 platform-specific.
type: boolean
nativeUserMode:
description: Enables native user mode. Currently works only on OpenShift
and DevWorkspace engine. Native User mode uses OpenShift OAuth
directly as identity provider, without Keycloak.
type: boolean
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to
setup identity federation on the OpenShift side. Auto-generated

View File

@ -62,6 +62,20 @@ spec:
are about to use. See also all the other fields starting with:
`identityProvider`.'
type: boolean
gatewayAuthenticationSidecarImage:
description: Gateway sidecar responsible for authentication when
NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy]
or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy].
type: string
gatewayAuthorizationSidecarImage:
description: Gateway sidecar responsible for authorization when
NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy]
or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy]
type: string
gatewayHeaderRewriteSidecarImage:
description: Header Rewrite Proxy sidecar image is used to properly
set authorization header. See link:https://github.com/che-incubator/header-rewrite-proxy[header-rewrite-proxy]
type: string
identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator
user. Defaults to `admin`.
@ -209,6 +223,11 @@ spec:
in 'openshift-config' namespace by Operator. Note that this
solution is Openshift 4 platform-specific.
type: boolean
nativeUserMode:
description: Enables native user mode. Currently works only on
OpenShift and DevWorkspace engine. Native User mode uses OpenShift
OAuth directly as identity provider, without Keycloak.
type: boolean
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used
to setup identity federation on the OpenShift side. Auto-generated

View File

@ -86,13 +86,13 @@ metadata:
categories: Developer Tools
certified: "false"
containerImage: quay.io/eclipse/che-operator:next
createdAt: "2021-07-01T21:02:20Z"
createdAt: "2021-07-02T08:14:01Z"
description: A Kube-native development solution that delivers portable and collaborative
developer workspaces.
operatorframework.io/suggested-namespace: eclipse-che
repository: https://github.com/eclipse-che/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-kubernetes.v7.33.0-246.nightly
name: eclipse-che-preview-kubernetes.v7.33.0-248.nightly
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -792,31 +792,13 @@ spec:
- update
- watch
- apiGroups:
- che.eclipse.org
- org.eclipse.che
resources:
- '*'
- checlusters
- checlusters/status
- checlusters/finalizers
verbs:
- '*'
- apiGroups:
- che.eclipse.org
resources:
- chemanagers
verbs:
- '*'
- apiGroups:
- che.eclipse.org
resources:
- chemanagers/status
verbs:
- get
- patch
- update
- apiGroups:
- che.eclipse.org
resources:
- chemanagers/finalizers
verbs:
- update
- apiGroups:
- controller.devfile.io
resources:
@ -985,6 +967,12 @@ spec:
value: quay.io/devfile/devworkspace-controller:next
- name: RELATED_IMAGE_internal_rest_backup_server
value: quay.io/eclipse/che-backup-server-rest:eeacd92
- name: RELATED_IMAGE_gateway_authentication_sidecar
value: quay.io/openshift/origin-oauth-proxy:4.7
- name: RELATED_IMAGE_gateway_authorization_sidecar
value: quay.io/openshift/origin-kube-rbac-proxy:4.7
- name: RELATED_IMAGE_gateway_header_sidecar
value: quay.io/che-incubator/header-rewrite-proxy:latest
- name: CHE_FLAVOR
value: che
- name: CONSOLE_LINK_NAME
@ -1057,7 +1045,7 @@ spec:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: devworkspace-operator
value: devworkspace-che-operator
- name: MAX_CONCURRENT_RECONCILES
value: "1"
- name: CONTROLLER_SERVICE_ACCOUNT_NAME
@ -1253,4 +1241,4 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.33.0-246.nightly
version: 7.33.0-248.nightly

View File

@ -62,6 +62,20 @@ spec:
are about to use. See also all the other fields starting with:
`identityProvider`.'
type: boolean
gatewayAuthenticationSidecarImage:
description: Gateway sidecar responsible for authentication when
NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy]
or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy].
type: string
gatewayAuthorizationSidecarImage:
description: Gateway sidecar responsible for authorization when
NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy]
or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy]
type: string
gatewayHeaderRewriteSidecarImage:
description: Header Rewrite Proxy sidecar image is used to properly
set authorization header. See link:https://github.com/che-incubator/header-rewrite-proxy[header-rewrite-proxy]
type: string
identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator
user. Defaults to `admin`.
@ -209,6 +223,11 @@ spec:
in 'openshift-config' namespace by Operator. Note that this
solution is Openshift 4 platform-specific.
type: boolean
nativeUserMode:
description: Enables native user mode. Currently works only on
OpenShift and DevWorkspace engine. Native User mode uses OpenShift
OAuth directly as identity provider, without Keycloak.
type: boolean
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used
to setup identity federation on the OpenShift side. Auto-generated

View File

@ -77,13 +77,13 @@ metadata:
categories: Developer Tools, OpenShift Optional
certified: "false"
containerImage: quay.io/eclipse/che-operator:next
createdAt: "2021-07-01T21:02:26Z"
createdAt: "2021-07-02T08:14:06Z"
description: A Kube-native development solution that delivers portable and collaborative
developer workspaces in OpenShift.
operatorframework.io/suggested-namespace: eclipse-che
repository: https://github.com/eclipse-che/che-operator
support: Eclipse Foundation
name: eclipse-che-preview-openshift.v7.33.0-246.nightly
name: eclipse-che-preview-openshift.v7.33.0-248.nightly
namespace: placeholder
spec:
apiservicedefinitions: {}
@ -861,31 +861,13 @@ spec:
- update
- watch
- apiGroups:
- che.eclipse.org
- org.eclipse.che
resources:
- '*'
- checlusters
- checlusters/status
- checlusters/finalizers
verbs:
- '*'
- apiGroups:
- che.eclipse.org
resources:
- chemanagers
verbs:
- '*'
- apiGroups:
- che.eclipse.org
resources:
- chemanagers/status
verbs:
- get
- patch
- update
- apiGroups:
- che.eclipse.org
resources:
- chemanagers/finalizers
verbs:
- update
- apiGroups:
- controller.devfile.io
resources:
@ -1052,6 +1034,12 @@ spec:
value: quay.io/devfile/devworkspace-controller:next
- name: RELATED_IMAGE_internal_rest_backup_server
value: quay.io/eclipse/che-backup-server-rest:eeacd92
- name: RELATED_IMAGE_gateway_authentication_sidecar
value: quay.io/openshift/origin-oauth-proxy:4.7
- name: RELATED_IMAGE_gateway_authorization_sidecar
value: quay.io/openshift/origin-kube-rbac-proxy:4.7
- name: RELATED_IMAGE_gateway_header_sidecar
value: quay.io/che-incubator/header-rewrite-proxy:latest
- name: CHE_FLAVOR
value: che
- name: CONSOLE_LINK_NAME
@ -1126,7 +1114,7 @@ spec:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: devworkspace-operator
value: devworkspace-che-operator
- name: MAX_CONCURRENT_RECONCILES
value: "1"
- name: CONTROLLER_SERVICE_ACCOUNT_NAME
@ -1330,4 +1318,4 @@ spec:
maturity: stable
provider:
name: Eclipse Foundation
version: 7.33.0-246.nightly
version: 7.33.0-248.nightly

View File

@ -64,6 +64,20 @@ spec:
are about to use. See also all the other fields starting with:
`identityProvider`.'
type: boolean
gatewayAuthenticationSidecarImage:
description: Gateway sidecar responsible for authentication
when NativeUserMode is enabled. See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy]
or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy].
type: string
gatewayAuthorizationSidecarImage:
description: Gateway sidecar responsible for authorization when
NativeUserMode is enabled. See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy]
or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy]
type: string
gatewayHeaderRewriteSidecarImage:
description: Header Rewrite Proxy sidecar image is used to properly
set authorization header. See link:https://github.com/che-incubator/header-rewrite-proxy[header-rewrite-proxy]
type: string
identityProviderAdminUserName:
description: Overrides the name of the Identity Provider administrator
user. Defaults to `admin`.
@ -214,6 +228,11 @@ spec:
secret in 'openshift-config' namespace by Operator. Note that
this solution is Openshift 4 platform-specific.
type: boolean
nativeUserMode:
description: Enables native user mode. Currently works only
on OpenShift and DevWorkspace engine. Native User mode uses
OpenShift OAuth directly as identity provider, without Keycloak.
type: boolean
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used
to setup identity federation on the OpenShift side. Auto-generated

View File

@ -85,6 +85,12 @@ spec:
value: quay.io/devfile/devworkspace-controller:next
- name: RELATED_IMAGE_internal_rest_backup_server
value: quay.io/eclipse/che-backup-server-rest:eeacd92
- name: RELATED_IMAGE_gateway_authentication_sidecar
value: quay.io/openshift/origin-oauth-proxy:4.7
- name: RELATED_IMAGE_gateway_authorization_sidecar
value: quay.io/openshift/origin-kube-rbac-proxy:4.7
- name: RELATED_IMAGE_gateway_header_sidecar
value: quay.io/che-incubator/header-rewrite-proxy:latest
- name: CHE_FLAVOR
value: che
- name: CONSOLE_LINK_NAME
@ -151,7 +157,7 @@ spec:
fieldRef:
fieldPath: metadata.name
- name: OPERATOR_NAME
value: devworkspace-operator
value: devworkspace-che-operator
- name: MAX_CONCURRENT_RECONCILES
value: "1"
- name: CONTROLLER_SERVICE_ACCOUNT_NAME

View File

@ -457,6 +457,21 @@ type CheClusterSpecAuth struct {
// Identity provider container custom settings.
// +optional
IdentityProviderContainerResources ResourcesCustomSettings `json:"identityProviderContainerResources,omitempty"`
// Enables native user mode. Currently works only on OpenShift and DevWorkspace engine.
// Native User mode uses OpenShift OAuth directly as identity provider, without Keycloak.
// +optional
NativeUserMode *bool `json:"nativeUserMode,omitempty"`
// Gateway sidecar responsible for authentication when NativeUserMode is enabled.
// See link:https://github.com/oauth2-proxy/oauth2-proxy[oauth2-proxy] or link:https://github.com/openshift/oauth-proxy[openshift/oauth-proxy].
// +optional
GatewayAuthenticationSidecarImage string `json:"gatewayAuthenticationSidecarImage,omitempty"`
// Gateway sidecar responsible for authorization when NativeUserMode is enabled.
// See link:https://github.com/brancz/kube-rbac-proxy[kube-rbac-proxy] or link:https://github.com/openshift/kube-rbac-proxy[openshift/kube-rbac-proxy]
// +optional
GatewayAuthorizationSidecarImage string `json:"gatewayAuthorizationSidecarImage,omitempty"`
// Header Rewrite Proxy sidecar image is used to properly set authorization header.
// See link:https://github.com/che-incubator/header-rewrite-proxy[header-rewrite-proxy]
GatewayHeaderRewriteSidecarImage string `json:"gatewayHeaderRewriteSidecarImage,omitempty"`
}
// Ingress custom settings, can be extended in the future

View File

@ -420,6 +420,11 @@ func (in *CheClusterSpecAuth) DeepCopyInto(out *CheClusterSpecAuth) {
in.IdentityProviderIngress.DeepCopyInto(&out.IdentityProviderIngress)
in.IdentityProviderRoute.DeepCopyInto(&out.IdentityProviderRoute)
out.IdentityProviderContainerResources = in.IdentityProviderContainerResources
if in.NativeUserMode != nil {
in, out := &in.NativeUserMode, &out.NativeUserMode
*out = new(bool)
**out = **in
}
return
}

View File

@ -519,6 +519,14 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
return reconcile.Result{RequeueAfter: time.Second}, err
}
if done, err = r.reconcileGatewayPermissions(deployContext); !done {
if err != nil {
logrus.Error(err)
}
// reconcile after 1 seconds since we deal with cluster objects
return reconcile.Result{RequeueAfter: time.Second}, err
}
done, err = r.reconcileWorkspacePermissions(deployContext)
if !done {
if err != nil {
@ -846,6 +854,12 @@ func (r *ReconcileChe) reconcileFinalizers(deployContext *deploy.DeployContext)
}
}
if util.IsNativeUserModeEnabled(deployContext.CheCluster) {
if _, err := r.reconcileGatewayPermissionsFinalizers(deployContext); err != nil {
logrus.Error(err)
}
}
if _, err := r.reconcileWorkspacePermissionsFinalizers(deployContext); err != nil {
logrus.Error(err)
}

View File

@ -139,19 +139,22 @@ func (r *ReconcileChe) GenerateAndSaveFields(deployContext *deploy.DeployContext
return err
}
}
keycloakRealm := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
if len(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm) < 1 {
deployContext.CheCluster.Spec.Auth.IdentityProviderRealm = keycloakRealm
if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak realm", keycloakRealm); err != nil {
return err
}
}
keycloakClientId := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
if len(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId) < 1 {
deployContext.CheCluster.Spec.Auth.IdentityProviderClientId = keycloakClientId
if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak client ID", keycloakClientId); err != nil {
return err
if !util.IsNativeUserModeEnabled(deployContext.CheCluster) {
keycloakRealm := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm, cheFlavor)
if len(deployContext.CheCluster.Spec.Auth.IdentityProviderRealm) < 1 {
deployContext.CheCluster.Spec.Auth.IdentityProviderRealm = keycloakRealm
if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak realm", keycloakRealm); err != nil {
return err
}
}
keycloakClientId := util.GetValue(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId, cheFlavor+"-public")
if len(deployContext.CheCluster.Spec.Auth.IdentityProviderClientId) < 1 {
deployContext.CheCluster.Spec.Auth.IdentityProviderClientId = keycloakClientId
if err := deploy.UpdateCheCRSpec(deployContext, "Keycloak client ID", keycloakClientId); err != nil {
return err
}
}
}
}

View File

@ -0,0 +1,79 @@
package che
import (
orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/gateway"
"github.com/eclipse-che/che-operator/pkg/util"
rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
CheGatewayClusterPermissionsFinalizerName = "cheGateway.clusterpermissions.finalizers.che.eclipse.org"
)
func (r *ReconcileChe) reconcileGatewayPermissions(deployContext *deploy.DeployContext) (bool, error) {
if util.IsNativeUserModeEnabled(deployContext.CheCluster) {
name := gatewayPermisisonsName(deployContext.CheCluster)
if _, err := deploy.SyncClusterRoleToCluster(deployContext, name, getGatewayClusterRoleRules()); err != nil {
return false, err
}
if _, err := deploy.SyncClusterRoleBindingToCluster(deployContext, name, gateway.GatewayServiceName, name); err != nil {
return false, err
}
if err := deploy.AppendFinalizer(deployContext, CheGatewayClusterPermissionsFinalizerName); err != nil {
return false, err
}
} else {
return deleteGatewayPermissions(deployContext)
}
return true, nil
}
func (r *ReconcileChe) reconcileGatewayPermissionsFinalizers(deployContext *deploy.DeployContext) (bool, error) {
if !deployContext.CheCluster.ObjectMeta.DeletionTimestamp.IsZero() {
return deleteGatewayPermissions(deployContext)
}
return true, nil
}
func deleteGatewayPermissions(deployContext *deploy.DeployContext) (bool, error) {
name := gatewayPermisisonsName(deployContext.CheCluster)
if done, err := deploy.Delete(deployContext, types.NamespacedName{Name: name}, &rbac.ClusterRole{}); !done {
return false, err
}
if done, err := deploy.Delete(deployContext, types.NamespacedName{Name: name}, &rbac.ClusterRoleBinding{}); !done {
return false, err
}
if err := deploy.DeleteFinalizer(deployContext, CheGatewayClusterPermissionsFinalizerName); err != nil {
return false, err
}
return true, nil
}
func gatewayPermisisonsName(instance *orgv1.CheCluster) string {
return instance.Namespace + "-" + gateway.GatewayServiceName
}
func getGatewayClusterRoleRules() []rbac.PolicyRule {
return []rbac.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"authentication.k8s.io"},
Resources: []string{"tokenreviews"},
},
{
Verbs: []string{"create"},
APIGroups: []string{"authorization.k8s.io"},
Resources: []string{"subjectaccessreviews"},
},
}
}

View File

@ -41,6 +41,9 @@ var (
defaultSingleHostGatewayImage string
defaultSingleHostGatewayConfigSidecarImage string
defaultInternalRestBackupServerImage string
defaultGatewayAuthenticationSidecarImage string
defaultGatewayAuthorizationSidecarImage string
defaultGatewayHeaderProxySidecarImage string
defaultCheWorkspacePluginBrokerMetadataImage string
defaultCheWorkspacePluginBrokerArtifactsImage string
@ -181,6 +184,8 @@ func InitDefaultsFromFile(defaultsPath string) {
defaultKeycloakImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_keycloak"))
defaultSingleHostGatewayImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway"))
defaultSingleHostGatewayConfigSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar"))
defaultGatewayAuthenticationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar"))
defaultGatewayAuthorizationSidecarImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar"))
defaultCheWorkspacePluginBrokerMetadataImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_che_workspace_plugin_broker_metadata"))
defaultCheWorkspacePluginBrokerArtifactsImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_che_workspace_plugin_broker_artifacts"))
defaultCheServerSecureExposerJwtProxyImage = util.GetDeploymentEnv(operatorDeployment, util.GetArchitectureDependentEnv("RELATED_IMAGE_che_server_secure_exposer_jwt_proxy_image"))
@ -335,6 +340,18 @@ func DefaultInternalBackupServerImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultInternalRestBackupServerImage)
}
func DefaultGatewayAuthenticationSidecarImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultGatewayAuthenticationSidecarImage)
}
func DefaultGatewayAuthorizationSidecarImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultGatewayAuthorizationSidecarImage)
}
func DefaultGatewayHeaderProxySidecarImage(cr *orgv1.CheCluster) string {
return patchDefaultImageName(cr, defaultGatewayHeaderProxySidecarImage)
}
func DefaultKubernetesImagePullerOperatorCSV() string {
return KubernetesImagePullerOperatorCSV
}
@ -440,6 +457,9 @@ func InitDefaultsFromEnv() {
defaultSingleHostGatewayImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway"))
defaultSingleHostGatewayConfigSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_single_host_gateway_config_sidecar"))
defaultInternalRestBackupServerImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_internal_rest_backup_server"))
defaultGatewayAuthenticationSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authentication_sidecar"))
defaultGatewayAuthorizationSidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_authorization_sidecar"))
defaultGatewayHeaderProxySidecarImage = getDefaultFromEnv(util.GetArchitectureDependentEnv("RELATED_IMAGE_gateway_header_sidecar"))
// CRW images for that are mentioned in the Che server che.properties
// For CRW these should be synced by hand with images stored in RH registries

View File

@ -13,8 +13,12 @@ package gateway
import (
"context"
"encoding/base64"
"fmt"
"strconv"
"github.com/sirupsen/logrus"
"github.com/eclipse-che/che-operator/pkg/deploy"
orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1"
@ -39,6 +43,7 @@ const (
gatewayServerConfigName = "che-gateway-route-server"
gatewayConfigComponentName = "che-gateway-config"
gatewayOauthSecretName = "che-gateway-oauth-secret"
)
var (
@ -50,6 +55,7 @@ var (
cmpopts.IgnoreFields(corev1.ServiceSpec{}, "ClusterIP"),
}
configMapDiffOpts = cmpopts.IgnoreFields(corev1.ConfigMap{}, "TypeMeta", "ObjectMeta")
secretDiffOpts = cmpopts.IgnoreFields(corev1.Secret{}, "TypeMeta", "ObjectMeta")
)
// SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration
@ -79,6 +85,25 @@ func syncAll(deployContext *deploy.DeployContext) error {
return err
}
if util.IsNativeUserModeEnabled(instance) {
if oauthSecret, err := getGatewaySecretSpec(deployContext); err == nil {
if _, err := deploy.Sync(deployContext, oauthSecret, secretDiffOpts); err != nil {
return err
}
oauthProxyConfig := getGatewayOauthProxyConfigSpec(instance, string(oauthSecret.Data["cookie_secret"]))
if _, err := deploy.Sync(deployContext, &oauthProxyConfig, configMapDiffOpts); err != nil {
return err
}
} else {
return err
}
headerRewriteProxyConfig := getGatewayHeaderRewriteProxyConfigSpec(instance)
if _, err := deploy.Sync(deployContext, &headerRewriteProxyConfig, configMapDiffOpts); err != nil {
return err
}
}
traefikConfig := getGatewayTraefikConfigSpec(instance)
if _, err := deploy.Sync(deployContext, &traefikConfig, configMapDiffOpts); err != nil {
return err
@ -102,6 +127,39 @@ func syncAll(deployContext *deploy.DeployContext) error {
return nil
}
func getGatewaySecretSpec(deployContext *deploy.DeployContext) (*corev1.Secret, error) {
secret := &corev1.Secret{}
exists, err := deploy.GetNamespacedObject(deployContext, gatewayOauthSecretName, secret)
if err == nil && exists {
if _, ok := secret.Data["cookie_secret"]; !ok {
logrus.Info("che-gateway-secret found, but does not contain `cookie_secret` value. Regenerating...")
return generateOauthSecretSpec(deployContext), nil
}
return secret, nil
} else if err == nil && !exists {
return generateOauthSecretSpec(deployContext), nil
} else {
return nil, err
}
}
func generateOauthSecretSpec(deployContext *deploy.DeployContext) *corev1.Secret {
return &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Name: gatewayOauthSecretName,
Namespace: deployContext.CheCluster.Namespace,
Labels: deploy.GetLabels(deployContext.CheCluster, GatewayServiceName),
},
Data: map[string][]byte{
"cookie_secret": generateRandomCookieSecret(),
},
}
}
func deleteAll(deployContext *deploy.DeployContext) error {
instance := deployContext.CheCluster
clusterAPI := deployContext.ClusterAPI
@ -318,7 +376,70 @@ func getGatewayRoleBindingSpec(instance *orgv1.CheCluster) rbac.RoleBinding {
}
}
func getGatewayOauthProxyConfigSpec(instance *orgv1.CheCluster, cookieSecret string) corev1.ConfigMap {
return corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Name: "che-gateway-config-oauth-proxy",
Namespace: instance.Namespace,
Labels: deploy.GetLabels(instance, GatewayServiceName),
},
Data: map[string]string{
"oauth-proxy.cfg": fmt.Sprintf(`
http_address = ":8080"
https_address = ""
provider = "openshift"
redirect_url = "https://%s/oauth/callback"
upstreams = [
"http://127.0.0.1:8081/"
]
client_id = "%s"
client_secret = "%s"
scope = "user:full"
openshift_service_account = "%s"
cookie_secret = "%s"
email_domains = "*"
cookie_httponly = false
pass_access_token = true
skip_provider_button = true`, instance.Spec.Server.CheHost, instance.Spec.Auth.OAuthClientName, instance.Spec.Auth.OAuthSecret, GatewayServiceName, cookieSecret),
},
}
}
func generateRandomCookieSecret() []byte {
return []byte(base64.StdEncoding.EncodeToString([]byte(util.GeneratePasswd(16))))
}
func getGatewayHeaderRewriteProxyConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap {
return corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Name: "che-gateway-config-header-rewrite-proxy",
Namespace: instance.Namespace,
Labels: deploy.GetLabels(instance, GatewayServiceName),
},
Data: map[string]string{
"rules.yaml": `
rules:
- from: X-Forwarded-Access-Token
to: Authorization
prefix: 'Bearer '
`,
},
}
}
func getGatewayTraefikConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap {
traefikPort := 8080
if util.IsNativeUserModeEnabled(instance) {
traefikPort = 8088
}
return corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
@ -330,16 +451,16 @@ func getGatewayTraefikConfigSpec(instance *orgv1.CheCluster) corev1.ConfigMap {
Labels: deploy.GetLabels(instance, GatewayServiceName),
},
Data: map[string]string{
"traefik.yml": `
"traefik.yml": fmt.Sprintf(`
entrypoints:
http:
address: ":8080"
forwardedHeaders:
insecure: true
https:
address: ":8443"
address: ":%d"
forwardedHeaders:
insecure: true
sink:
address: ":8090"
ping:
entryPoint: "sink"
global:
checkNewVersion: false
sendAnonymousUsage: false
@ -348,19 +469,15 @@ providers:
directory: "/dynamic-config"
watch: true
log:
level: "INFO"`,
level: "INFO"`, traefikPort),
},
}
}
func getGatewayDeploymentSpec(instance *orgv1.CheCluster) appsv1.Deployment {
gatewayImage := util.GetValue(instance.Spec.Server.SingleHostGatewayImage, deploy.DefaultSingleHostGatewayImage(instance))
sidecarImage := util.GetValue(instance.Spec.Server.SingleHostGatewayConfigSidecarImage, deploy.DefaultSingleHostGatewayConfigSidecarImage(instance))
configLabelsMap := util.GetMapValue(instance.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels)
terminationGracePeriodSeconds := int64(10)
configLabels := labels.FormatLabels(configLabelsMap)
labels, labelsSelector := deploy.GetLabelsAndSelector(instance, GatewayServiceName)
deployLabels, labelsSelector := deploy.GetLabelsAndSelector(instance, GatewayServiceName)
return appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
@ -370,7 +487,7 @@ func getGatewayDeploymentSpec(instance *orgv1.CheCluster) appsv1.Deployment {
ObjectMeta: metav1.ObjectMeta{
Name: GatewayServiceName,
Namespace: instance.Namespace,
Labels: labels,
Labels: deployLabels,
},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{
@ -381,81 +498,169 @@ func getGatewayDeploymentSpec(instance *orgv1.CheCluster) appsv1.Deployment {
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Labels: deployLabels,
},
Spec: corev1.PodSpec{
TerminationGracePeriodSeconds: &terminationGracePeriodSeconds,
ServiceAccountName: GatewayServiceName,
RestartPolicy: corev1.RestartPolicyAlways,
Containers: []corev1.Container{
{
Name: "gateway",
Image: gatewayImage,
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: []corev1.VolumeMount{
{
Name: "static-config",
MountPath: "/etc/traefik",
},
{
Name: "dynamic-config",
MountPath: "/dynamic-config",
},
},
},
{
Name: "configbump",
Image: sidecarImage,
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: []corev1.VolumeMount{
{
Name: "dynamic-config",
MountPath: "/dynamic-config",
},
},
Env: []corev1.EnvVar{
{
Name: "CONFIG_BUMP_DIR",
Value: "/dynamic-config",
},
{
Name: "CONFIG_BUMP_LABELS",
Value: configLabels,
},
{
Name: "CONFIG_BUMP_NAMESPACE",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.namespace",
},
},
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "static-config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "che-gateway-config",
},
},
},
},
{
Name: "dynamic-config",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
Containers: getContainersSpec(instance),
Volumes: getVolumesSpec(instance),
},
},
},
}
}
func getContainersSpec(instance *orgv1.CheCluster) []corev1.Container {
configLabelsMap := util.GetMapValue(instance.Spec.Server.SingleHostGatewayConfigMapLabels, deploy.DefaultSingleHostGatewayConfigMapLabels)
gatewayImage := util.GetValue(instance.Spec.Server.SingleHostGatewayImage, deploy.DefaultSingleHostGatewayImage(instance))
configSidecarImage := util.GetValue(instance.Spec.Server.SingleHostGatewayConfigSidecarImage, deploy.DefaultSingleHostGatewayConfigSidecarImage(instance))
authnImage := util.GetValue(instance.Spec.Auth.GatewayAuthenticationSidecarImage, deploy.DefaultGatewayAuthenticationSidecarImage(instance))
authzImage := util.GetValue(instance.Spec.Auth.GatewayAuthorizationSidecarImage, deploy.DefaultGatewayAuthorizationSidecarImage(instance))
headerProxyImage := util.GetValue(instance.Spec.Auth.GatewayHeaderRewriteSidecarImage, deploy.DefaultGatewayHeaderProxySidecarImage(instance))
configLabels := labels.FormatLabels(configLabelsMap)
containers := []corev1.Container{
{
Name: "gateway",
Image: gatewayImage,
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: []corev1.VolumeMount{
{
Name: "static-config",
MountPath: "/etc/traefik",
},
{
Name: "dynamic-config",
MountPath: "/dynamic-config",
},
},
},
{
Name: "configbump",
Image: configSidecarImage,
ImagePullPolicy: corev1.PullAlways,
VolumeMounts: []corev1.VolumeMount{
{
Name: "dynamic-config",
MountPath: "/dynamic-config",
},
},
Env: []corev1.EnvVar{
{
Name: "CONFIG_BUMP_DIR",
Value: "/dynamic-config",
},
{
Name: "CONFIG_BUMP_LABELS",
Value: configLabels,
},
{
Name: "CONFIG_BUMP_NAMESPACE",
ValueFrom: &corev1.EnvVarSource{
FieldRef: &corev1.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.namespace",
},
},
},
},
},
}
if util.IsNativeUserModeEnabled(instance) {
containers = append(containers,
corev1.Container{
Name: "oauth-proxy",
Image: authnImage,
ImagePullPolicy: corev1.PullAlways,
Args: []string{
"--config=/etc/oauth-proxy/oauth-proxy.cfg",
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "oauth-proxy-config",
MountPath: "/etc/oauth-proxy",
},
},
Ports: []corev1.ContainerPort{
{ContainerPort: 8080},
},
},
corev1.Container{
Name: "header-rewrite-proxy",
Image: headerProxyImage,
ImagePullPolicy: corev1.PullAlways,
Args: []string{"--upstream=http://127.0.0.1:8088", "--bind=127.0.0.1:8081", "--rules=/etc/rules/rules.yaml"},
VolumeMounts: []corev1.VolumeMount{
{
Name: "header-rewrite-proxy-rules",
MountPath: "/etc/rules",
},
},
},
corev1.Container{
Name: "kube-rbac-proxy",
Image: authzImage,
ImagePullPolicy: corev1.PullAlways,
Args: []string{
"--insecure-listen-address=127.0.0.1:8089",
"--upstream=http://127.0.0.1:8090/ping",
"--logtostderr=true",
"--v=10",
},
})
}
return containers
}
func getVolumesSpec(instance *orgv1.CheCluster) []corev1.Volume {
volumes := []corev1.Volume{
{
Name: "static-config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "che-gateway-config",
},
},
},
},
{
Name: "dynamic-config",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
}
if util.IsNativeUserModeEnabled(instance) {
volumes = append(volumes, corev1.Volume{
Name: "oauth-proxy-config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "che-gateway-config-oauth-proxy",
},
},
},
})
volumes = append(volumes, corev1.Volume{
Name: "header-rewrite-proxy-rules",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "che-gateway-config-header-rewrite-proxy",
},
},
},
})
}
return volumes
}
func getGatewayServiceSpec(instance *orgv1.CheCluster) corev1.Service {
@ -480,12 +685,6 @@ func getGatewayServiceSpec(instance *orgv1.CheCluster) corev1.Service {
Protocol: corev1.ProtocolTCP,
TargetPort: intstr.FromInt(8080),
},
{
Name: "gateway-https",
Port: 8443,
Protocol: corev1.ProtocolTCP,
TargetPort: intstr.FromInt(8443),
},
},
},
}

View File

@ -0,0 +1,166 @@
package gateway
import (
"context"
"encoding/base64"
"testing"
orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1"
"github.com/eclipse-che/che-operator/pkg/deploy"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func TestSyncAllToCluster(t *testing.T) {
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
deployContext := &deploy.DeployContext{
CheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "single-host",
},
},
},
ClusterAPI: deploy.ClusterAPI{
Client: cli,
NonCachedClient: cli,
Scheme: scheme.Scheme,
},
}
err := SyncGatewayToCluster(deployContext)
if err != nil {
t.Fatalf("Failed to sync Gateway: %v", err)
}
deployment := &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, deployment)
if err != nil {
t.Fatalf("Failed to get deployment: %v", err)
}
if len(deployment.Spec.Template.Spec.Containers) != 2 {
t.Fatalf("With classic multi-user, there should be 2 containers in the gateway, traefik and configbump. But it has '%d' containers.", len(deployment.Spec.Template.Spec.Containers))
}
service := &corev1.Service{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, service)
if err != nil {
t.Fatalf("Failed to get service: %v", err)
}
}
func TestNativeUserGateway(t *testing.T) {
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
nativeUserMode := true
deployContext := &deploy.DeployContext{
CheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Auth: orgv1.CheClusterSpecAuth{
NativeUserMode: &nativeUserMode,
},
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "single-host",
},
},
},
ClusterAPI: deploy.ClusterAPI{
Client: cli,
NonCachedClient: cli,
Scheme: scheme.Scheme,
},
}
err := SyncGatewayToCluster(deployContext)
if err != nil {
t.Fatalf("Failed to sync Gateway: %v", err)
}
deployment := &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, deployment)
if err != nil {
t.Fatalf("Failed to get deployment: %v", err)
}
if len(deployment.Spec.Template.Spec.Containers) != 5 {
t.Fatalf("With native user mode, there should be 5 containers in the gateway.. But it has '%d' containers.", len(deployment.Spec.Template.Spec.Containers))
}
service := &corev1.Service{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, service)
if err != nil {
t.Fatalf("Failed to get service: %v", err)
}
}
func TestNoGatewayForMultiHost(t *testing.T) {
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
corev1.SchemeBuilder.AddToScheme(scheme.Scheme)
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
deployContext := &deploy.DeployContext{
CheCluster: &orgv1.CheCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: "eclipse-che",
Name: "eclipse-che",
},
Spec: orgv1.CheClusterSpec{
Server: orgv1.CheClusterSpecServer{
ServerExposureStrategy: "multi-host",
},
},
},
ClusterAPI: deploy.ClusterAPI{
Client: cli,
NonCachedClient: cli,
Scheme: scheme.Scheme,
},
}
err := SyncGatewayToCluster(deployContext)
if err != nil {
t.Fatalf("Failed to sync Gateway: %v", err)
}
deployment := &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, deployment)
if err == nil {
t.Fatalf("Failed to get deployment: %v", err)
} else {
if v, ok := err.(errors.APIStatus); ok {
if v.Status().Code != 404 {
t.Fatalf("Deployment should not be found, thus code 404, but got '%d'", v.Status().Code)
}
} else {
t.Fatalf("Wrong error returned.")
}
}
}
func TestRandomCookieSecret(t *testing.T) {
secret := generateRandomCookieSecret()
if len(secret) != 24 {
t.Fatalf("lenght of the secret should be 24")
}
_, err := base64.StdEncoding.Decode(make([]byte, 24), secret)
if err != nil {
t.Fatalf("Failed to decode the secret '%s'", err)
}
}

View File

@ -0,0 +1,21 @@
//
// Copyright (c) 2020-2020 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors:
// Red Hat, Inc. - initial API and implementation
//
package gateway
import "github.com/eclipse-che/che-operator/pkg/deploy"
func init() {
err := deploy.InitTestDefaultsFromDeployment("../../../deploy/operator.yaml")
if err != nil {
panic(err)
}
}

View File

@ -16,13 +16,14 @@ import (
"errors"
"strings"
"github.com/sirupsen/logrus"
orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1"
"github.com/eclipse-che/che-operator/pkg/deploy"
"github.com/eclipse-che/che-operator/pkg/deploy/expose"
"github.com/eclipse-che/che-operator/pkg/util"
"github.com/google/go-cmp/cmp/cmpopts"
oauth "github.com/openshift/api/oauth/v1"
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
@ -47,7 +48,9 @@ var (
// the provisioning is complete, false if requeue of the reconcile request is needed.
func SyncIdentityProviderToCluster(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster
if cr.Spec.Auth.ExternalIdentityProvider {
if util.IsNativeUserModeEnabled(cr) {
return syncNativeIdentityProviderItems(deployContext)
} else if cr.Spec.Auth.ExternalIdentityProvider {
return true, nil
}
@ -154,31 +157,41 @@ func syncOpenShiftIdentityProvider(deployContext *deploy.DeployContext) (bool, e
return true, nil
}
func syncNativeIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster
if err := resolveOpenshiftOAuthClientName(deployContext); err != nil {
return false, err
}
if err := resolveOpenshiftOAuthClientSecret(deployContext); err != nil {
return false, err
}
redirectURIs := []string{"https://" + cr.Spec.Server.CheHost + "/oauth/callback"}
oAuthClient := deploy.GetOAuthClientSpec(cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret, redirectURIs)
provisioned, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
if !provisioned {
return false, err
}
return true, nil
}
func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bool, error) {
cr := deployContext.CheCluster
oAuthClientName := cr.Spec.Auth.OAuthClientName
if len(oAuthClientName) < 1 {
oAuthClientName = cr.Name + "-openshift-identity-provider-" + strings.ToLower(util.GeneratePasswd(6))
cr.Spec.Auth.OAuthClientName = oAuthClientName
if err := deploy.UpdateCheCRSpec(deployContext, "oAuthClient name", oAuthClientName); err != nil {
return false, err
}
if err := resolveOpenshiftOAuthClientName(deployContext); err != nil {
return false, err
}
oauthSecret := cr.Spec.Auth.OAuthSecret
if len(oauthSecret) < 1 {
oauthSecret = util.GeneratePasswd(12)
cr.Spec.Auth.OAuthSecret = oauthSecret
if err := deploy.UpdateCheCRSpec(deployContext, "oAuth secret name", oauthSecret); err != nil {
return false, err
}
if err := resolveOpenshiftOAuthClientSecret(deployContext); err != nil {
return false, err
}
keycloakURL := cr.Spec.Auth.IdentityProviderURL
cheFlavor := deploy.DefaultCheFlavor(cr)
keycloakRealm := util.GetValue(cr.Spec.Auth.IdentityProviderRealm, cheFlavor)
oAuthClient := deploy.GetOAuthClientSpec(oAuthClientName, oauthSecret, keycloakURL, keycloakRealm, util.IsOpenShift4)
oAuthClient := deploy.GetKeycloakOAuthClientSpec(cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret, keycloakURL, keycloakRealm, util.IsOpenShift4)
provisioned, err := deploy.Sync(deployContext, oAuthClient, oAuthClientDiffOpts)
if !provisioned {
return false, err
@ -192,7 +205,7 @@ func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bo
cr,
deploy.IdentityProviderName,
func(cr *orgv1.CheCluster) (string, error) {
return GetOpenShiftIdentityProviderProvisionCommand(cr, oAuthClientName, oauthSecret)
return GetOpenShiftIdentityProviderProvisionCommand(cr, cr.Spec.Auth.OAuthClientName, cr.Spec.Auth.OAuthSecret)
},
"Create OpenShift identity provider")
if err != nil {
@ -214,6 +227,32 @@ func SyncOpenShiftIdentityProviderItems(deployContext *deploy.DeployContext) (bo
return true, nil
}
func resolveOpenshiftOAuthClientName(deployContext *deploy.DeployContext) error {
cr := deployContext.CheCluster
oAuthClientName := cr.Spec.Auth.OAuthClientName
if len(oAuthClientName) < 1 {
oAuthClientName = cr.Name + "-openshift-identity-provider-" + strings.ToLower(util.GeneratePasswd(6))
cr.Spec.Auth.OAuthClientName = oAuthClientName
if err := deploy.UpdateCheCRSpec(deployContext, "oAuthClient name", oAuthClientName); err != nil {
return err
}
}
return nil
}
func resolveOpenshiftOAuthClientSecret(deployContext *deploy.DeployContext) error {
cr := deployContext.CheCluster
oauthSecret := cr.Spec.Auth.OAuthSecret
if len(oauthSecret) < 1 {
oauthSecret = util.GeneratePasswd(12)
cr.Spec.Auth.OAuthSecret = oauthSecret
if err := deploy.UpdateCheCRSpec(deployContext, "oAuth secret name", oauthSecret); err != nil {
return err
}
}
return nil
}
// SyncGitHubOAuth provisions GitHub OAuth if secret with annotation
// `che.eclipse.org/github-oauth-credentials=true` or `che.eclipse.org/oauth-scm-configuration=github`
// is mounted into a container

View File

@ -23,7 +23,7 @@ const (
OAuthFinalizerName = "oauthclients.finalizers.che.eclipse.org"
)
func GetOAuthClientSpec(name string, oauthSecret string, keycloakURL string, keycloakRealm string, isOpenShift4 bool) *oauth.OAuthClient {
func GetKeycloakOAuthClientSpec(name string, oauthSecret string, keycloakURL string, keycloakRealm string, isOpenShift4 bool) *oauth.OAuthClient {
providerName := "openshift-v3"
if isOpenShift4 {
providerName = "openshift-v4"
@ -41,6 +41,10 @@ func GetOAuthClientSpec(name string, oauthSecret string, keycloakURL string, key
"https://" + keycloakURL + redirectURLSuffix,
}
}
return GetOAuthClientSpec(name, oauthSecret, redirectURIs)
}
func GetOAuthClientSpec(name string, oauthSecret string, redirectURIs []string) *oauth.OAuthClient {
return &oauth.OAuthClient{
TypeMeta: metav1.TypeMeta{
Kind: "OAuthClient",

View File

@ -161,6 +161,13 @@ func (s Server) getDeploymentSpec() (*appsv1.Deployment, error) {
FieldPath: "metadata.namespace"}},
})
if util.IsNativeUserModeEnabled(s.deployContext.CheCluster) {
cheEnv = append(cheEnv, corev1.EnvVar{
Name: "CHE_AUTH_NATIVEUSER",
Value: "true",
})
}
cheImageAndTag := GetFullCheServerImageLink(s.deployContext.CheCluster)
pullPolicy := corev1.PullPolicy(util.GetValue(string(s.deployContext.CheCluster.Spec.Server.CheImagePullPolicy), deploy.DefaultPullPolicyFromDockerImage(cheImageAndTag)))

View File

@ -447,6 +447,11 @@ func NewBoolPointer(value bool) *bool {
return &variable
}
func IsNativeUserModeEnabled(c *orgv1.CheCluster) bool {
// Native user mode is now available only on openshift
return IsOpenShift && c.Spec.Auth.NativeUserMode != nil && *c.Spec.Auth.NativeUserMode
}
// IsOAuthEnabled returns true when oAuth is enable for CheCluster resource, otherwise false.
func IsOAuthEnabled(c *orgv1.CheCluster) bool {
return IsOpenShift && c.Spec.Auth.OpenShiftoAuth != nil && *c.Spec.Auth.OpenShiftoAuth