Skip to content

Commit

Permalink
Inject certificate to http client from a configmap referenced in the …
Browse files Browse the repository at this point in the history
…config

Signed-off-by: ivinokur <[email protected]>
  • Loading branch information
vinokurig committed May 14, 2024
1 parent 04f5369 commit c2b142c
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 14 deletions.
11 changes: 10 additions & 1 deletion apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package v1alpha1

import (
"fmt"

dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -61,6 +60,9 @@ type RoutingConfig struct {
// DevWorkspaces. However, changing the proxy configuration for the DevWorkspace Operator itself
// requires restarting the controller deployment.
ProxyConfig *Proxy `json:"proxyConfig,omitempty"`
// TLSCertificateConfigmapRef defines the name and namespace of the configmap with a certificate to inject into the
// HTTP client.
TLSCertificateConfigmapRef *ConfigmapReference `json:"tlsCertificateConfigmapRef,omitempty"`
}

type WorkspaceConfig struct {
Expand Down Expand Up @@ -240,6 +242,13 @@ type ProjectCloneConfig struct {
Env []corev1.EnvVar `json:"env,omitempty"`
}

type ConfigmapReference struct {
// Name is the name of the configmap
Name string `json:"name"`
// Namespace is the namespace of the configmap
Namespace string `json:"namespace"`
}

// DevWorkspaceOperatorConfig is the Schema for the devworkspaceoperatorconfigs API
// +kubebuilder:object:root=true
// +kubebuilder:resource:path=devworkspaceoperatorconfigs,scope=Namespaced,shortName=dwoc
Expand Down
4 changes: 2 additions & 2 deletions controllers/workspace/devworkspace_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ func (r *DevWorkspaceReconciler) Reconcile(ctx context.Context, req ctrl.Request
reqLogger = reqLogger.WithValues(constants.DevWorkspaceIDLoggerKey, workspace.Status.DevWorkspaceId)
reqLogger.Info("Reconciling Workspace", "resolvedConfig", configString)

setupHttpClients(r.Client, config, r.Log)

// Check if the DevWorkspaceRouting instance is marked to be deleted, which is
// indicated by the deletion timestamp being set.
if workspace.GetDeletionTimestamp() != nil {
Expand Down Expand Up @@ -668,8 +670,6 @@ func (r *DevWorkspaceReconciler) getWorkspaceId(ctx context.Context, workspace *
}

func (r *DevWorkspaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
setupHttpClients()

maxConcurrentReconciles, err := wkspConfig.GetMaxConcurrentReconciles()
if err != nil {
return err
Expand Down
61 changes: 50 additions & 11 deletions controllers/workspace/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@
package controllers

import (
"context"
"crypto/tls"
"crypto/x509"
"k8s.io/apimachinery/pkg/types"
"net/http"
"net/url"
"time"

"github.com/devfile/devworkspace-operator/pkg/config"
controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"golang.org/x/net/http/httpproxy"
)

Expand All @@ -28,25 +36,28 @@ var (
healthCheckHttpClient *http.Client
)

func setupHttpClients() {
func setupHttpClients(k8s client.Client, config *controller.OperatorConfiguration, logger logr.Logger) {
transport := http.DefaultTransport.(*http.Transport).Clone()
if certs, ok := readCertificates(k8s, config, logger); ok {
for _, certsPem := range certs {
injectCertificates([]byte(certsPem), transport)
}
}
healthCheckTransport := http.DefaultTransport.(*http.Transport).Clone()
healthCheckTransport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}

globalConfig := config.GetGlobalConfig()

if globalConfig.Routing != nil && globalConfig.Routing.ProxyConfig != nil {
if config.Routing != nil && config.Routing.ProxyConfig != nil {
proxyConf := httpproxy.Config{}
if globalConfig.Routing.ProxyConfig.HttpProxy != nil {
proxyConf.HTTPProxy = *globalConfig.Routing.ProxyConfig.HttpProxy
if config.Routing.ProxyConfig.HttpProxy != nil {
proxyConf.HTTPProxy = *config.Routing.ProxyConfig.HttpProxy
}
if globalConfig.Routing.ProxyConfig.HttpsProxy != nil {
proxyConf.HTTPSProxy = *globalConfig.Routing.ProxyConfig.HttpsProxy
if config.Routing.ProxyConfig.HttpsProxy != nil {
proxyConf.HTTPSProxy = *config.Routing.ProxyConfig.HttpsProxy
}
if globalConfig.Routing.ProxyConfig.NoProxy != nil {
proxyConf.NoProxy = *globalConfig.Routing.ProxyConfig.NoProxy
if config.Routing.ProxyConfig.NoProxy != nil {
proxyConf.NoProxy = *config.Routing.ProxyConfig.NoProxy
}

proxyFunc := func(req *http.Request) (*url.URL, error) {
Expand All @@ -64,3 +75,31 @@ func setupHttpClients() {
Timeout: 500 * time.Millisecond,
}
}

func readCertificates(k8s client.Client, config *controller.OperatorConfiguration, logger logr.Logger) (map[string]string, bool) {
configmapRef := config.Routing.TLSCertificateConfigmapRef
if configmapRef == nil {
return nil, false
}
configMap := &corev1.ConfigMap{}
namespacedName := &types.NamespacedName{
Name: configmapRef.Name,
Namespace: configmapRef.Namespace,
}
err := k8s.Get(context.Background(), *namespacedName, configMap)
if err != nil {
logger.Error(err, "Failed to read configmap with certificates")
return nil, false
}
return configMap.Data, true
}

func injectCertificates(certsPem []byte, transport *http.Transport) {
caCertPool := transport.TLSClientConfig.RootCAs
if caCertPool == nil {
caCertPool = x509.NewCertPool()
}
if ok := caCertPool.AppendCertsFromPEM(certsPem); ok {
transport.TLSClientConfig = &tls.Config{RootCAs: caCertPool}
}
}
28 changes: 28 additions & 0 deletions pkg/config/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
}
to.Routing.ProxyConfig = proxy.MergeProxyConfigs(from.Routing.ProxyConfig, defaultConfig.Routing.ProxyConfig)
}
if from.Routing.TLSCertificateConfigmapRef != nil {
if to.Routing.TLSCertificateConfigmapRef == nil {
to.Routing.TLSCertificateConfigmapRef = &controller.ConfigmapReference{}
}
to.Routing.TLSCertificateConfigmapRef = mergeTLSCertificateConfigmapRef(from.Routing.TLSCertificateConfigmapRef, defaultConfig.Routing.TLSCertificateConfigmapRef)
}
}
if from.Workspace != nil {
if to.Workspace == nil {
Expand Down Expand Up @@ -426,6 +432,28 @@ func mergeContainerSecurityContext(base, patch *corev1.SecurityContext) *corev1.
return patched
}

func mergeTLSCertificateConfigmapRef(operatorConfig, clusterConfig *controller.ConfigmapReference) *controller.ConfigmapReference {
if clusterConfig == nil {
return operatorConfig
}
if operatorConfig == nil {
return clusterConfig
}
mergedConfigmapReference := &controller.ConfigmapReference{
Name: operatorConfig.Name,
Namespace: operatorConfig.Namespace,
}

if mergedConfigmapReference.Name == "" {
mergedConfigmapReference.Name = clusterConfig.Name
}
if mergedConfigmapReference.Namespace == "" {
mergedConfigmapReference.Namespace = clusterConfig.Namespace
}

return mergedConfigmapReference
}

func mergeResources(from, to *corev1.ResourceRequirements) *corev1.ResourceRequirements {
result := to.DeepCopy()
if from.Limits != nil {
Expand Down

0 comments on commit c2b142c

Please sign in to comment.