Skip to content

Commit

Permalink
Respect openshift cluster wide proxy
Browse files Browse the repository at this point in the history
Signed-off-by: Anatoliy Bazko <abazko@redhat.com>
  • Loading branch information
tolusha committed May 21, 2020
1 parent e7ed1e5 commit ed7ee06
Show file tree
Hide file tree
Showing 22 changed files with 731 additions and 286 deletions.
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"CONSOLE_LINK_IMAGE": "/dashboard/assets/branding/loader.svg",
"CHE_IDENTITY_SECRET": "che-identity-secret",
"CHE_IDENTITY_POSTGRES_SECRET": "che-identity-postgres-secret",
"CHE_POSTGRES_SECRET": "che-postgres-secret"
"CHE_POSTGRES_SECRET": "che-postgres-secret",
"CHE_SERVER_CERT_CONFIG_MAP": "ca-certs"
},
"cwd": "${workspaceFolder}",
"args": [
Expand Down
9 changes: 9 additions & 0 deletions deploy/cluster_role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@ rules:
resources:
- infrastructures
- oauths
- proxies
verbs:
- get
- list
- watch
- apiGroups:
- user.openshift.io
resources:
Expand All @@ -49,3 +52,9 @@ rules:
- update
- patch
- delete
- apiGroups:
- ''
resources:
- configmaps
verbs:
- get
2 changes: 2 additions & 0 deletions deploy/operator-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,5 @@ spec:
value: che-identity-postgres-secret
- name: CHE_POSTGRES_SECRET
value: che-postgres-secret
- name: CHE_SERVER_CERT_CONFIG_MAP
value: ca-certs
2 changes: 2 additions & 0 deletions deploy/operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ spec:
value: che-identity-postgres-secret
- name: CHE_POSTGRES_SECRET
value: che-postgres-secret
- name: CHE_SERVER_CERT_CONFIG_MAP
value: ca-certs
restartPolicy: Always
serviceAccountName: che-operator
terminationGracePeriodSeconds: 5
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ replace (
)

require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/docker/spdystream v0.0.0-00010101000000-000000000000 // indirect
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 // indirect
github.com/go-logr/logr v0.0.0-00010101000000-000000000000 // indirect
Expand All @@ -78,6 +79,7 @@ require (
github.com/onsi/gomega v1.10.0 // indirect
github.com/openshift/api v0.0.0-00010101000000-000000000000
github.com/operator-framework/operator-sdk v0.0.0-00010101000000-000000000000
github.com/pborman/uuid v1.2.0 // indirect
github.com/peterbourgon/diskv v0.0.0-00010101000000-000000000000 // indirect
github.com/prometheus/common v0.4.1
github.com/sirupsen/logrus v1.4.2
Expand Down
132 changes: 62 additions & 70 deletions pkg/controller/che/che_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,20 @@ import (
"encoding/pem"
"fmt"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"

orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"github.com/eclipse/che-operator/pkg/deploy"
"github.com/eclipse/che-operator/pkg/util"
configv1 "github.com/openshift/api/config/v1"
oauthv1 "github.com/openshift/api/config/v1"
consolev1 "github.com/openshift/api/console/v1"
oauth "github.com/openshift/api/oauth/v1"
routev1 "github.com/openshift/api/route/v1"
userv1 "github.com/openshift/api/user/v1"
"github.com/sirupsen/logrus"
"golang.org/x/net/http/httpproxy"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
Expand Down Expand Up @@ -105,6 +103,9 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
if err := oauthv1.AddToScheme(mgr.GetScheme()); err != nil {
logrus.Errorf("Failed to add OpenShift OAuth to scheme: %s", err)
}
if err := configv1.AddToScheme(mgr.GetScheme()); err != nil {
logrus.Errorf("Failed to add OpenShift Config to scheme: %s", err)
}
if hasConsolelinkObject() {
if err := consolev1.AddToScheme(mgr.GetScheme()); err != nil {
logrus.Errorf("Failed to add OpenShift ConsoleLink to scheme: %s", err)
Expand Down Expand Up @@ -243,6 +244,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
Client: r.client,
Scheme: r.scheme,
}

// Fetch the CheCluster instance
tests := r.tests
instance, err := r.GetCR(request)
Expand Down Expand Up @@ -316,6 +318,47 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
}

proxy, err := util.ReadCheClusterProxyConfiguration(instance)
if isOpenShift4 {
clusterProxy := &configv1.Proxy{}
if err := r.client.Get(context.TODO(), types.NamespacedName{Name: "cluster"}, clusterProxy); err != nil {
logrus.Errorf("Failed to get OpenShift cluster-wide proxy configuration: %v", err)
return reconcile.Result{RequeueAfter: time.Second * 1}, err
}

if clusterProxy.Status.HTTPProxy != "" {
proxy, err = util.ReadClusterWideProxyConfiguration(clusterProxy)
if err != nil {
logrus.Errorf("Failed to parse OpenShift cluster-wide proxy configuration: %v", err)
return reconcile.Result{RequeueAfter: time.Second * 1}, err
}

if proxy.TrustedCAMapName != "" {
proxyCA, err := k8sclient.ReadOpenshiftConfigMap(proxy.TrustedCAMapName)
if err != nil {
logrus.Errorf("Failed to read configmap %s, error: %v", proxy.TrustedCAMapName, err)
return reconcile.Result{RequeueAfter: time.Second * 1}, err
}

if instance.Spec.Server.ServerTrustStoreConfigMapName == "" {
instance.Spec.Server.ServerTrustStoreConfigMapName = deploy.DefaultCheServerCertConfigMap()
if err := r.UpdateCheCRSpec(instance, "Server Trust Store configmap", instance.Spec.Server.ServerTrustStoreConfigMapName); err != nil {
return reconcile.Result{Requeue: true, RequeueAfter: time.Second * 1}, err
}
}
certConfigMap, err := deploy.SyncTrustStoreConfigMapToCluster(instance, proxyCA.Data, clusterAPI)
if certConfigMap == nil {
if err != nil {
logrus.Error(err)
} else {
logrus.Infof("Waiting on '%s' config map be created", instance.Spec.Server.ServerTrustStoreConfigMapName)
}
return reconcile.Result{}, err
}
}
}
}

// Handle Che TLS certificates on Kubernetes like infrastructures
if instance.Spec.Server.TlsSupport && instance.Spec.Server.SelfSignedCert && !isOpenShift {
// Ensure TLS configuration is correct
Expand Down Expand Up @@ -404,7 +447,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
// and NOT from the Openshift API Master URL (as in v3)
// So we also need the self-signed certificate to access them (same as the Che server)
(isOpenShift4 && instance.Spec.Auth.OpenShiftoAuth && !instance.Spec.Server.TlsSupport) {
if err := r.CreateTLSSecret(instance, "", "self-signed-certificate", clusterAPI); err != nil {
if err := r.CreateTLSSecret(instance, "", "self-signed-certificate", proxy, clusterAPI); err != nil {
return reconcile.Result{}, err
}
}
Expand All @@ -425,7 +468,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
if err != nil {
logrus.Errorf("Failed to get OpenShift cluster public hostname. A secret with API crt will not be created and consumed by RH-SSO/Keycloak")
} else {
if err := r.CreateTLSSecret(instance, baseURL, "openshift-api-crt", clusterAPI); err != nil {
if err := r.CreateTLSSecret(instance, baseURL, "openshift-api-crt", proxy, clusterAPI); err != nil {
return reconcile.Result{}, err
}
}
Expand Down Expand Up @@ -793,7 +836,7 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
}
}

deploymentStatus := deploy.SyncKeycloakDeploymentToCluster(instance, clusterAPI)
deploymentStatus := deploy.SyncKeycloakDeploymentToCluster(instance, proxy, clusterAPI)
if !tests {
if !deploymentStatus.Continue {
logrus.Info("Waiting on deployment 'keycloak' to be ready")
Expand Down Expand Up @@ -1078,16 +1121,16 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e

// create Che ConfigMap which is synced with CR and is not supposed to be manually edited
// controller will reconcile this CM with CR spec
cheEnv := deploy.GetConfigMapData(instance)
configMapStatus := deploy.SyncConfigMapToCluster(instance, cheEnv, clusterAPI)
cheConfigMap, err := deploy.SyncCheConfigMapToCluster(instance, proxy, clusterAPI)
if !tests {
if !configMapStatus.Continue {
logrus.Infof("Waiting on config map '%s' to be created", cheFlavor)
if configMapStatus.Err != nil {
logrus.Error(configMapStatus.Err)
if cheConfigMap == nil {
if err != nil {
logrus.Error(err)
} else {
logrus.Infof("Waiting on config map '%s' to be created", deploy.CheConfigMapName)
}

return reconcile.Result{Requeue: configMapStatus.Requeue}, configMapStatus.Err
return reconcile.Result{}, err
}
}

Expand All @@ -1097,11 +1140,11 @@ func (r *ReconcileChe) Reconcile(request reconcile.Request) (reconcile.Result, e
if tests {
cmResourceVersion = r.GetEffectiveConfigMap(instance, deploy.CheConfigMapName).ResourceVersion
} else {
cmResourceVersion = configMapStatus.ConfigMap.ResourceVersion
cmResourceVersion = cheConfigMap.ResourceVersion
}

// Create a new che deployment
deploymentStatus := deploy.SyncCheDeploymentToCluster(instance, cmResourceVersion, clusterAPI)
deploymentStatus := deploy.SyncCheDeploymentToCluster(instance, cmResourceVersion, proxy, clusterAPI)
if !tests {
if !deploymentStatus.Continue {
logrus.Infof("Waiting on deployment '%s' to be ready", cheFlavor)
Expand Down Expand Up @@ -1242,7 +1285,7 @@ func createConsoleLink(isOpenShift4 bool, protocol string, instance *orgv1.CheCl
// GetEndpointTlsCrt creates a test TLS route and gets it to extract certificate chain
// There's an easier way which is to read tls secret in default (3.11) or openshift-ingress (4.0) namespace
// which however requires extra privileges for operator service account
func (r *ReconcileChe) GetEndpointTlsCrt(instance *orgv1.CheCluster, endpointUrl string, clusterAPI deploy.ClusterAPI) (certificate []byte, err error) {
func (r *ReconcileChe) GetEndpointTlsCrt(instance *orgv1.CheCluster, endpointUrl string, proxy *util.Proxy, clusterAPI deploy.ClusterAPI) (certificate []byte, err error) {
var requestURL string
var routeStatus deploy.RouteProvisioningStatus

Expand All @@ -1265,9 +1308,9 @@ func (r *ReconcileChe) GetEndpointTlsCrt(instance *orgv1.CheCluster, endpointUrl
//adding the proxy settings to the Transport object
transport := &http.Transport{}

if instance.Spec.Server.ProxyURL != "" {
logrus.Infof("Configuring proxy with %s to extract crt from the following URL: %s", instance.Spec.Server.ProxyURL, requestURL)
r.configureProxy(instance, transport)
if proxy.HttpProxy != "" {
logrus.Infof("Configuring proxy with %s to extract crt from the following URL: %s", proxy.HttpProxy, requestURL)
util.ConfigureProxy(instance, transport, proxy)
}

transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
Expand Down Expand Up @@ -1303,57 +1346,6 @@ func (r *ReconcileChe) GetEndpointTlsCrt(instance *orgv1.CheCluster, endpointUrl
return certificate, nil
}

func (r *ReconcileChe) configureProxy(instance *orgv1.CheCluster, transport *http.Transport) {
proxyParts := strings.Split(instance.Spec.Server.ProxyURL, "://")
proxyProtocol := ""
proxyHost := ""
if len(proxyParts) == 1 {
proxyProtocol = ""
proxyHost = proxyParts[0]
} else {
proxyProtocol = proxyParts[0]
proxyHost = proxyParts[1]

}

proxyURL := proxyHost
if instance.Spec.Server.ProxyPort != "" {
proxyURL = proxyURL + ":" + instance.Spec.Server.ProxyPort
}

proxyUser := instance.Spec.Server.ProxyUser
proxyPassword := instance.Spec.Server.ProxyPassword
proxySecret := instance.Spec.Server.ProxySecret
user, password, err := k8sclient.ReadSecret(proxySecret, instance.Namespace)
if err == nil {
proxyUser = user
proxyPassword = password
} else {
logrus.Errorf("Failed to read '%s' secret: %s", proxySecret, err)
}
if len(proxyUser) > 1 && len(proxyPassword) > 1 {
proxyURL = proxyUser + ":" + proxyPassword + "@" + proxyURL
}

if proxyProtocol != "" {
proxyURL = proxyProtocol + "://" + proxyURL
}
config := httpproxy.Config{
HTTPProxy: proxyURL,
HTTPSProxy: proxyURL,
NoProxy: strings.Replace(instance.Spec.Server.NonProxyHosts, "|", ",", -1),
}
proxyFunc := config.ProxyFunc()
transport.Proxy = func(r *http.Request) (*url.URL, error) {
theProxyUrl, err := proxyFunc(r.URL)
if err != nil {
logrus.Warnf("Error when trying to get the proxy to access TLS endpoint URL: %s - %s", r.URL, err)
}
logrus.Infof("Using proxy: %s to access TLS endpoint URL: %s", theProxyUrl, r.URL)
return theProxyUrl, err
}
}

func hasConsolelinkObject() bool {
resourceList, err := util.GetServerResources()
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/controller/che/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,13 @@ func (r *ReconcileChe) CreateSecret(instance *orgv1.CheCluster, m map[string][]b
return nil
}

func (r *ReconcileChe) CreateTLSSecret(instance *orgv1.CheCluster, url string, name string, clusterAPI deploy.ClusterAPI) (err error) {
func (r *ReconcileChe) CreateTLSSecret(instance *orgv1.CheCluster, url string, name string, proxy *util.Proxy, clusterAPI deploy.ClusterAPI) (err error) {
// create a secret with either router tls cert (or OpenShift API crt) when on OpenShift infra
// and router is configured with a self signed certificate
// this secret is used by CRW server to reach RH SSO TLS endpoint
secret := &corev1.Secret{}
if err := r.client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: instance.Namespace}, secret); err != nil && errors.IsNotFound(err) {
crt, err := r.GetEndpointTlsCrt(instance, url, clusterAPI)
crt, err := r.GetEndpointTlsCrt(instance, url, proxy, clusterAPI)
if err != nil {
logrus.Errorf("Failed to extract crt for secret %s. Failed to create a secret with a self signed crt: %s", name, err)
return err
Expand Down
Loading

0 comments on commit ed7ee06

Please sign in to comment.