feat(oauth-proxy): configuration enhancements for k8s config (#1400)

* feat(oauth-proxy): configuration enhancements for k8s config
pull/1410/head
Piotr Karatkevich 2022-06-22 09:51:43 +03:00 committed by GitHub
parent b2f337c978
commit c364ba4e93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 479 additions and 12 deletions

View File

@ -286,6 +286,8 @@ func TestConvertFrom(t *testing.T) {
IdentityProviderURL: "IdentityProviderURL",
OAuthClientName: "OAuthClientName",
OAuthSecret: "OAuthSecret",
OAuthScope: "OAuthScope",
IdentityToken: "IdentityToken",
Gateway: chev2.Gateway{
Deployment: chev2.Deployment{
Containers: []chev2.Container{
@ -377,6 +379,8 @@ func TestConvertFrom(t *testing.T) {
assert.Equal(t, checlusterv1.Spec.Auth.IdentityProviderURL, "IdentityProviderURL")
assert.Equal(t, checlusterv1.Spec.Auth.OAuthClientName, "OAuthClientName")
assert.Equal(t, checlusterv1.Spec.Auth.OAuthSecret, "OAuthSecret")
assert.Equal(t, checlusterv1.Spec.Auth.OAuthScope, "OAuthScope")
assert.Equal(t, checlusterv1.Spec.Auth.IdentityToken, "IdentityToken")
assert.Equal(t, checlusterv1.Spec.Database.ChePostgresContainerResources.Limits.Cpu, "2")
assert.Equal(t, checlusterv1.Spec.Database.ChePostgresContainerResources.Limits.Memory, "228Mi")

View File

@ -245,6 +245,8 @@ func TestConvertTo(t *testing.T) {
IdentityProviderURL: "IdentityProviderURL",
OAuthClientName: "OAuthClientName",
OAuthSecret: "OAuthSecret",
OAuthScope: "OAuthScope",
IdentityToken: "IdentityToken",
GatewayAuthenticationSidecarImage: "GatewayAuthenticationSidecarImage",
GatewayAuthorizationSidecarImage: "GatewayAuthorizationSidecarImage",
},
@ -304,6 +306,8 @@ func TestConvertTo(t *testing.T) {
assert.Equal(t, checlusterv2.Spec.Networking.Auth.IdentityProviderURL, "IdentityProviderURL")
assert.Equal(t, checlusterv2.Spec.Networking.Auth.OAuthClientName, "OAuthClientName")
assert.Equal(t, checlusterv2.Spec.Networking.Auth.OAuthSecret, "OAuthSecret")
assert.Equal(t, checlusterv2.Spec.Networking.Auth.OAuthScope, "OAuthScope")
assert.Equal(t, checlusterv2.Spec.Networking.Auth.IdentityToken, "IdentityToken")
assert.Equal(t, checlusterv2.Spec.ContainerRegistry.Hostname, "AirGapContainerRegistryHostname")
assert.Equal(t, checlusterv2.Spec.ContainerRegistry.Organization, "AirGapContainerRegistryOrganization")

View File

@ -265,6 +265,8 @@ func TestRoundConvertCheClusterV2(t *testing.T) {
IdentityProviderURL: "IdentityProviderURL",
OAuthClientName: "OAuthClientName",
OAuthSecret: "OAuthSecret",
OAuthScope: "OAuthScope",
IdentityToken: "IdentityToken",
Gateway: chev2.Gateway{
Deployment: chev2.Deployment{
Containers: []chev2.Container{
@ -466,6 +468,8 @@ func TestRoundConvertCheClusterV1(t *testing.T) {
IdentityProviderURL: "IdentityProviderURL",
OAuthClientName: "OAuthClientName",
OAuthSecret: "OAuthSecret",
OAuthScope: "OAuthScope",
IdentityToken: "IdentityToken",
GatewayAuthenticationSidecarImage: "GatewayAuthenticationSidecarImage",
GatewayAuthorizationSidecarImage: "GatewayAuthorizationSidecarImage",
},

View File

@ -241,6 +241,8 @@ func (dst *CheCluster) convertFrom_Auth(src *chev2.CheCluster) error {
dst.Spec.Auth.IdentityProviderURL = src.Spec.Networking.Auth.IdentityProviderURL
dst.Spec.Auth.OAuthClientName = src.Spec.Networking.Auth.OAuthClientName
dst.Spec.Auth.OAuthSecret = src.Spec.Networking.Auth.OAuthSecret
dst.Spec.Auth.OAuthScope = src.Spec.Networking.Auth.OAuthScope
dst.Spec.Auth.IdentityToken = src.Spec.Networking.Auth.IdentityToken
for _, c := range src.Spec.Networking.Auth.Gateway.Deployment.Containers {
switch c.Name {

View File

@ -182,6 +182,8 @@ func (src *CheCluster) convertTo_Networking_Auth(dst *chev2.CheCluster) error {
dst.Spec.Networking.Auth.IdentityProviderURL = src.Spec.Auth.IdentityProviderURL
dst.Spec.Networking.Auth.OAuthClientName = src.Spec.Auth.OAuthClientName
dst.Spec.Networking.Auth.OAuthSecret = src.Spec.Auth.OAuthSecret
dst.Spec.Networking.Auth.OAuthScope = src.Spec.Auth.OAuthScope
dst.Spec.Networking.Auth.IdentityToken = src.Spec.Auth.IdentityToken
if err := src.convertTo_Networking_Auth_Gateway(dst); err != nil {
return err

View File

@ -499,6 +499,15 @@ type CheClusterSpecAuth struct {
// Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field.
// +optional
OAuthSecret string `json:"oAuthSecret,omitempty"`
// Access Token Scope.
// This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
// +optional
OAuthScope string `json:"oAuthScope,omitempty"`
// Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`.
// Default value is `id_token`.
// This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
// +optional
IdentityToken string `json:"identityToken,omitempty"`
// Deprecated. The value of this flag is ignored.
// Overrides the container image used in the Identity Provider, Keycloak or RH-SSO, deployment.
// This includes the image tag. Omit it or leave it empty to use the default container image provided by the Operator.

View File

@ -19,6 +19,9 @@ import (
"os"
"strings"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
"github.com/eclipse-che/che-operator/pkg/common/constants"
"k8s.io/apimachinery/pkg/api/resource"
imagepullerv1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1"
@ -351,6 +354,16 @@ type Auth struct {
OAuthClientName string `json:"oAuthClientName,omitempty"`
// Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
OAuthSecret string `json:"oAuthSecret,omitempty"`
// Access Token Scope.
// This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
// +optional
OAuthScope string `json:"oAuthScope,omitempty"`
// Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`.
// Default value is `id_token`.
// This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
// +optional
// +kubebuilder:validation:Enum=id_token;access_token
IdentityToken string `json:"identityToken,omitempty"`
// Gateway settings.
// +optional
Gateway Gateway `json:"gateway,omitempty"`
@ -631,3 +644,18 @@ func (c *CheCluster) GetDefaultNamespace() string {
return "<username>-" + os.Getenv("CHE_FLAVOR")
}
func (c *CheCluster) GetIdentityToken() string {
if len(c.Spec.Networking.Auth.IdentityToken) > 0 {
return c.Spec.Networking.Auth.IdentityToken
}
if infrastructure.IsOpenShift() {
return constants.AccessToken
}
return constants.IdToken
}
func (c *CheCluster) IsAccessTokenConfigured() bool {
return c.GetIdentityToken() == constants.AccessToken
}

View File

@ -0,0 +1,153 @@
//
// Copyright (c) 2019-2021 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 v2
import (
"reflect"
"testing"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
"github.com/stretchr/testify/assert"
)
func TestIsAccesTokenConfigured(t *testing.T) {
t.Run("TestIsAccesTokenConfigured when access_token defined", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{
IdentityToken: "access_token",
},
}},
}
assert.True(t, cheCluster.IsAccessTokenConfigured(), "'access_token' should be activated")
})
t.Run("TestIsAccesTokenConfigured when id_token defined", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{
IdentityToken: "id_token",
},
}},
}
assert.False(t, cheCluster.IsAccessTokenConfigured(), "'access_token' should not be activated")
})
}
func TestGetIdentityToken(t *testing.T) {
t.Run("TestGetIdentityToken when access_token defined in config and k8s", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{
IdentityToken: "access_token",
},
}},
}
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
assert.Equal(t, "access_token", cheCluster.GetIdentityToken(),
"'access_token' should be used")
})
t.Run("TestGetIdentityToken when id_token defined in config and k8s", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{
IdentityToken: "id_token",
},
}},
}
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
assert.Equal(t, "id_token", cheCluster.GetIdentityToken(),
"'id_token' should be used")
})
t.Run("TestGetIdentityToken when no defined token in config and k8s", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{},
}},
}
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
assert.Equal(t, "id_token", cheCluster.GetIdentityToken(),
"'id_token' should be used")
})
t.Run("TestGetIdentityToken when access_token defined in config and openshift", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{
IdentityToken: "access_token",
},
}},
}
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
assert.Equal(t, "access_token", cheCluster.GetIdentityToken(),
"'access_token' should be used")
})
t.Run("TestGetIdentityToken when id_token defined in config and openshift", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{
IdentityToken: "id_token",
},
}},
}
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
assert.Equal(t, "id_token", cheCluster.GetIdentityToken(),
"'id_token' should be used")
})
t.Run("TestGetIdentityToken when no defined token in config and openshift", func(t *testing.T) {
cheCluster := &CheCluster{
Spec: CheClusterSpec{
Networking: CheClusterSpecNetworking{
Auth: Auth{},
}},
}
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
assert.Equal(t, "access_token", cheCluster.GetIdentityToken(),
"'access_token' should be used")
})
}
func TestGetDefaultIdentityToken(t *testing.T) {
emptyCheCluster := CheCluster{}
var tests = []struct {
infrastructure infrastructure.Type
identityToken string
}{
{infrastructure.OpenShiftv4, "access_token"},
{infrastructure.Kubernetes, "id_token"},
{infrastructure.Unsupported, "id_token"},
}
for _, test := range tests {
infrastructure.InitializeForTesting(test.infrastructure)
if actual := emptyCheCluster.GetIdentityToken(); !reflect.DeepEqual(test.identityToken, actual) {
t.Errorf("Test Failed. Expected '%s', but got '%s'", test.identityToken, actual)
}
}
}

View File

@ -256,6 +256,12 @@ spec:
field. By default, this will be automatically calculated and
set by the Operator.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There
are two types of tokens supported: `id_token` and `access_token`.
Default value is `id_token`. This field is specific to Che
installations made for Kubernetes only and ignored for OpenShift.'
type: string
initialOpenShiftOAuthUser:
description: Deprecated. The value of this flag is ignored.
For operating with the OpenShift OAuth authentication, create
@ -279,6 +285,10 @@ spec:
to setup identity federation on the OpenShift side. Auto-generated
when left blank. See also the `OpenShiftoAuth` field.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che
installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient`
resource used to setup identity federation on the OpenShift
@ -2287,10 +2297,25 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There
are two types of tokens supported: `id_token` and `access_token`.
Default value is `id_token`. This field is specific to
Che installations made for Kubernetes only and ignored
for OpenShift.'
enum:
- id_token
- access_token
type: string
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource
used to set up identity federation on the OpenShift side.
type: string
oAuthScope:
description: Access Token Scope. This field is specific
to Che installations made for Kubernetes only and ignored
for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient`
resource used to set up identity federation on the OpenShift

View File

@ -234,6 +234,12 @@ spec:
By default, this will be automatically calculated and set by
the Operator.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are
two types of tokens supported: `id_token` and `access_token`.
Default value is `id_token`. This field is specific to Che installations
made for Kubernetes only and ignored for OpenShift.'
type: string
initialOpenShiftOAuthUser:
description: Deprecated. The value of this flag is ignored. For
operating with the OpenShift OAuth authentication, create a
@ -257,6 +263,10 @@ spec:
to setup identity federation on the OpenShift side. Auto-generated
when left blank. See also the `OpenShiftoAuth` field.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che
installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient`
resource used to setup identity federation on the OpenShift
@ -2232,10 +2242,24 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There
are two types of tokens supported: `id_token` and `access_token`.
Default value is `id_token`. This field is specific to Che
installations made for Kubernetes only and ignored for OpenShift.'
enum:
- id_token
- access_token
type: string
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource
used to set up identity federation on the OpenShift side.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to
Che installations made for Kubernetes only and ignored for
OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient`
resource used to set up identity federation on the OpenShift

View File

@ -475,9 +475,7 @@ func provisionMainWorkspaceRoute(cheCluster *chev2.CheCluster, routing *dwo.DevW
getServiceURL(wsGatewayPort, dwId, dwNamespace),
[]string{"/" + dwId})
if infrastructure.IsOpenShift() {
// on OpenShift, we need to set authorization header.
// This MUST come before Auth, because Auth needs Authorization header to be properly set.
if cheCluster.IsAccessTokenConfigured() {
cfg.AddAuthHeaderRewrite(dwId)
}

View File

@ -161,6 +161,9 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
type: string
initialOpenShiftOAuthUser:
description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific.
type: boolean
@ -170,6 +173,9 @@ spec:
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field.
type: string
@ -1581,9 +1587,18 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
enum:
- id_token
- access_token
type: string
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string

View File

@ -156,6 +156,9 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
type: string
initialOpenShiftOAuthUser:
description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific.
type: boolean
@ -165,6 +168,9 @@ spec:
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field.
type: string
@ -1576,9 +1582,18 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
enum:
- id_token
- access_token
type: string
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string

View File

@ -161,6 +161,9 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
type: string
initialOpenShiftOAuthUser:
description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific.
type: boolean
@ -170,6 +173,9 @@ spec:
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field.
type: string
@ -1581,9 +1587,18 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
enum:
- id_token
- access_token
type: string
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string

View File

@ -156,6 +156,9 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
type: string
initialOpenShiftOAuthUser:
description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific.
type: boolean
@ -165,6 +168,9 @@ spec:
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field.
type: string
@ -1576,9 +1582,18 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
enum:
- id_token
- access_token
type: string
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string

View File

@ -156,6 +156,9 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server (Keycloak / RH-SSO server). Set this ONLY when a use of an external Identity Provider is needed. See the `externalIdentityProvider` field. By default, this will be automatically calculated and set by the Operator.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
type: string
initialOpenShiftOAuthUser:
description: Deprecated. The value of this flag is ignored. For operating with the OpenShift OAuth authentication, create a new user account since the kubeadmin can not be used. If the value is true, then a new OpenShift OAuth user will be created for the HTPasswd identity provider. If the value is false and the user has already been created, then it will be removed. If value is an empty, then do nothing. The user's credentials are stored in the `openshift-oauth-user-credentials` secret in 'openshift-config' namespace by Operator. Note that this solution is Openshift 4 platform-specific.
type: boolean
@ -165,6 +168,9 @@ spec:
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OpenShiftoAuth` field.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to setup identity federation on the OpenShift side. Auto-generated when left blank. See also the `OAuthClientName` field.
type: string
@ -1576,9 +1582,18 @@ spec:
identityProviderURL:
description: Public URL of the Identity Provider server.
type: string
identityToken:
description: 'Identity token to be passed to upstream. There are two types of tokens supported: `id_token` and `access_token`. Default value is `id_token`. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.'
enum:
- id_token
- access_token
type: string
oAuthClientName:
description: Name of the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string
oAuthScope:
description: Access Token Scope. This field is specific to Che installations made for Kubernetes only and ignored for OpenShift.
type: string
oAuthSecret:
description: Name of the secret set in the OpenShift `OAuthClient` resource used to set up identity federation on the OpenShift side.
type: string

View File

@ -71,6 +71,8 @@ const (
GitLabOAuthConfigClientIdFileName = "id"
GitLabOAuthConfigClientSecretFileName = "secret"
OAuthScmConfiguration = "oauth-scm-configuration"
AccessToken = "access_token"
IdToken = "id_token"
// Labels
KubernetesComponentLabelKey = "app.kubernetes.io/component"

View File

@ -259,3 +259,13 @@ func FormatLabels(m map[string]string) string {
return labels.FormatLabels(m)
}
// Whitelists the host.
// Sample: Whitelist("che.yourcompany.com") -> ".yourcompany.com"
func Whitelist(hostname string) (value string) {
i := strings.Index(hostname, ".")
if i > -1 {
return hostname[i:]
}
return hostname
}

View File

@ -63,3 +63,21 @@ func TestGetImageNameAndTag(t *testing.T) {
}
}
}
func TestWhitelist(t *testing.T) {
var tests = []struct {
host string
whitelistedHost string
}{
{"che.qwruwqlrj.com", ".qwruwqlrj.com"},
{"one.two.three.four", ".two.three.four"},
{"abraCadabra-KvakaZybra", "abraCadabra-KvakaZybra"},
{".", "."},
{"", ""},
}
for _, test := range tests {
if actual := Whitelist(test.host); !reflect.DeepEqual(test.whitelistedHost, actual) {
t.Errorf("Test Failed. Expected '%s', but got '%s'", test.whitelistedHost, actual)
}
}
}

View File

@ -18,7 +18,6 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults"
"github.com/eclipse-che/che-operator/pkg/deploy"
@ -126,7 +125,7 @@ func (d *DashboardReconciler) createGatewayConfig(ctx *chetypes.DeployContext) *
10,
"http://"+d.getComponentName(ctx)+":8080",
[]string{})
if infrastructure.IsOpenShift() {
if ctx.CheCluster.IsAccessTokenConfigured() {
cfg.AddAuthHeaderRewrite(d.getComponentName(ctx))
}
return cfg

View File

@ -127,7 +127,7 @@ func syncAll(deployContext *chetypes.DeployContext) error {
return err
}
if infrastructure.IsOpenShift() {
if instance.IsAccessTokenConfigured() {
if headerRewritePluginConfig, err := getGatewayHeaderRewritePluginConfigSpec(instance); err == nil {
if _, err := deploy.Sync(deployContext, headerRewritePluginConfig, configMapDiffOpts); err != nil {
return err
@ -238,8 +238,10 @@ func getGatewayServerConfigSpec(deployContext *chetypes.DeployContext) (corev1.C
"http://"+deploy.CheServiceName+":8080",
[]string{})
if infrastructure.IsOpenShift() {
if deployContext.CheCluster.IsAccessTokenConfigured() {
cfg.AddAuthHeaderRewrite(serverComponentName)
}
if infrastructure.IsOpenShift() {
// native user mode is currently only available on OpenShift but let's be defensive here so that
// this doesn't break once we enable it on Kubernetes, too. Token check will have to work
// differently on Kuberentes.
@ -398,7 +400,7 @@ providers:
log:
level: "INFO"`, traefikPort)
if infrastructure.IsOpenShift() {
if instance.IsAccessTokenConfigured() {
data += `
experimental:
localPlugins:
@ -546,7 +548,7 @@ func getTraefikContainerVolumeMounts(instance *chev2.CheCluster) []corev1.Volume
MountPath: "/dynamic-config",
},
}
if infrastructure.IsOpenShift() {
if instance.IsAccessTokenConfigured() {
mounts = append(mounts, corev1.VolumeMount{
Name: "header-rewrite-traefik-plugin",
MountPath: "/plugins-local/src/github.com/che-incubator/header-rewrite-traefik-plugin",
@ -580,7 +582,7 @@ func getVolumesSpec(instance *chev2.CheCluster) []corev1.Volume {
getOauthProxyConfigVolume(),
getKubeRbacProxyConfigVolume())
if infrastructure.IsOpenShift() {
if instance.IsAccessTokenConfigured() {
volumes = append(volumes, corev1.Volume{
Name: "header-rewrite-traefik-plugin",
VolumeSource: corev1.VolumeSource{

View File

@ -26,6 +26,7 @@ import (
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
"github.com/eclipse-che/che-operator/pkg/common/constants"
defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults"
"github.com/eclipse-che/che-operator/pkg/common/utils"
"github.com/eclipse-che/che-operator/pkg/deploy"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -114,8 +115,11 @@ cookie_secret = "%s"
cookie_expire = "24h0m0s"
email_domains = "*"
cookie_httponly = false
pass_authorization_header = true
skip_provider_button = true
whitelist_domains = "%s"
cookie_domains = "%s"
%s
%s
%s
`, GatewayServicePort,
ctx.CheHost,
@ -123,7 +127,11 @@ skip_provider_button = true
ctx.CheCluster.Spec.Networking.Auth.OAuthClientName,
ctx.CheCluster.Spec.Networking.Auth.OAuthSecret,
cookieSecret,
skipAuthConfig(ctx.CheCluster))
utils.Whitelist(ctx.CheHost),
utils.Whitelist(ctx.CheHost),
skipAuthConfig(ctx.CheCluster),
identityTokenConfig(ctx.CheCluster),
oauthScopeConfig(ctx.CheCluster))
}
func skipAuthConfig(instance *chev2.CheCluster) string {
@ -147,6 +155,23 @@ func skipAuthConfig(instance *chev2.CheCluster) string {
return ""
}
func identityTokenConfig(instance *chev2.CheCluster) string {
if instance.IsAccessTokenConfigured() {
// pass OAuth access_token to upstream via X-Forwarded-Access-Token header
return "pass_access_token = true"
}
// pass OIDC IDToken to upstream via Authorization Bearer header
return "pass_authorization_header = true"
}
func oauthScopeConfig(instance *chev2.CheCluster) string {
scope := instance.Spec.Networking.Auth.OAuthScope
if len(scope) > 1 {
return fmt.Sprintf("scope = \"%s\"", scope)
}
return ""
}
func getOauthProxyContainerSpec(ctx *chetypes.DeployContext) corev1.Container {
// append env var with ConfigMap revision to restore pod automatically when config has been changed
cm := &corev1.ConfigMap{}

View File

@ -0,0 +1,83 @@
//
// Copyright (c) 2019-2021 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 (
"testing"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
chev2 "github.com/eclipse-che/che-operator/api/v2"
"github.com/eclipse-che/che-operator/pkg/common/test"
"github.com/stretchr/testify/assert"
)
func TestKubernetesOauthProxyConfig(t *testing.T) {
ctx := test.GetDeployContext(
&chev2.CheCluster{
Spec: chev2.CheClusterSpec{
Networking: chev2.CheClusterSpecNetworking{
Auth: chev2.Auth{
IdentityProviderURL: "http://bla.bla.bla/idp",
OAuthClientName: "client name",
OAuthSecret: "secret",
},
}},
}, nil)
ctx.CheHost = "che-site.che-domain.com"
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
config := kubernetesOauthProxyConfig(ctx, "blabol")
assert.Contains(t, config, "pass_authorization_header = true")
assert.Contains(t, config, "whitelist_domains = \".che-domain.com\"")
assert.Contains(t, config, "cookie_domains = \".che-domain.com\"")
assert.NotContains(t, config, "scope = ")
assert.NotContains(t, config, "pass_access_token = true")
}
func TestScopeDefinedForKubernetesOauthProxyConfig(t *testing.T) {
ctx := test.GetDeployContext(
&chev2.CheCluster{
Spec: chev2.CheClusterSpec{
Networking: chev2.CheClusterSpecNetworking{
Auth: chev2.Auth{
IdentityProviderURL: "http://bla.bla.bla/idp",
OAuthClientName: "client name",
OAuthSecret: "secret",
OAuthScope: "scope1 scope2 scope3 scope4 scope5",
},
}},
}, nil)
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
config := kubernetesOauthProxyConfig(ctx, "blabol")
assert.Contains(t, config, "scope = \"scope1 scope2 scope3 scope4 scope5\"")
}
func TestAccessTokenDefinedForKubernetesOauthProxyConfig(t *testing.T) {
ctx := test.GetDeployContext(
&chev2.CheCluster{
Spec: chev2.CheClusterSpec{
Networking: chev2.CheClusterSpecNetworking{
Auth: chev2.Auth{
IdentityProviderURL: "http://bla.bla.bla/idp",
OAuthClientName: "client name",
OAuthSecret: "secret",
IdentityToken: "access_token",
},
}},
}, nil)
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
config := kubernetesOauthProxyConfig(ctx, "blabol")
assert.Contains(t, config, "pass_access_token = true")
assert.NotContains(t, config, "pass_authorization_header = true")
}