-
Notifications
You must be signed in to change notification settings - Fork 72
fix: Use merged DWOC config for HTTP client certificate initialization #1645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
9c13dbf
de8aa1d
b1b3bac
d1fa4a5
8e5c4d5
2669f17
6fb6b83
1348318
271ee31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ import ( | |
| "net/url" | ||
| "time" | ||
|
|
||
| "github.com/devfile/devworkspace-operator/pkg/config" | ||
| controller "github.com/devfile/devworkspace-operator/apis/controller/v1alpha1" | ||
|
|
||
| "k8s.io/apimachinery/pkg/types" | ||
|
|
||
|
|
@@ -32,87 +32,111 @@ import ( | |
| "golang.org/x/net/http/httpproxy" | ||
| ) | ||
|
|
||
| var ( | ||
| httpClient *http.Client | ||
| type HttpClientsHolder interface { | ||
| GetHttpClient(routingConfig *controller.RoutingConfig) *http.Client | ||
| GetHealthCheckHttpClient(routingConfig *controller.RoutingConfig) *http.Client | ||
| } | ||
|
|
||
| type DefaultHttpsClientHolder struct { | ||
| k8s client.Client | ||
| logger logr.Logger | ||
|
|
||
| client *http.Client | ||
| healthCheckHttpClient *http.Client | ||
| ) | ||
| } | ||
|
|
||
| var httpClientsHolder HttpClientsHolder | ||
|
|
||
| func setupHttpClients(k8s client.Client, logger logr.Logger) { | ||
| func NewDefaultHttpsClientHolder(k8s client.Client, logger logr.Logger) *DefaultHttpsClientHolder { | ||
| return &DefaultHttpsClientHolder{k8s: k8s, logger: logger} | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment contradicts actual behavior The comment says "not an issue at all" but this IS an issue - the operator was configured to trust custom certificates. Rebuilding without them on transient failure breaks the trust chain. Consider updating this comment after fixing the transient failure handling. |
||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Transient ConfigMap failure silently strips custom certificates When A single transient Kubernetes API error will break TLS trust cluster-wide for all subsequent workspace operations. Suggested fix: On if routingConfig.TLSCertificateConfigmapRef != nil {
certsCM, err := h.readCertCM(ctx, routingConfig.TLSCertificateConfigmapRef)
if err != nil {
h.logger.Error(err, "Failed to read TLS certificate ConfigMap")
// Preserve existing HTTP clients on transient failure
return
}
newCertsCM = certsCM
} |
||
| func (h *DefaultHttpsClientHolder) GetHttpClient(routingConfig *controller.RoutingConfig) *http.Client { | ||
| transport := http.DefaultTransport.(*http.Transport).Clone() | ||
| healthCheckTransport := http.DefaultTransport.(*http.Transport).Clone() | ||
| healthCheckTransport.TLSClientConfig = &tls.Config{ | ||
| transport.Proxy = h.getProxyFunc(routingConfig) | ||
| transport.TLSClientConfig = &tls.Config{ | ||
| RootCAs: h.getCaCertPool(routingConfig), | ||
| } | ||
|
|
||
|
tolusha marked this conversation as resolved.
|
||
| return &http.Client{ | ||
| Transport: transport, | ||
| } | ||
| } | ||
|
|
||
| func (h *DefaultHttpsClientHolder) GetHealthCheckHttpClient(routingConfig *controller.RoutingConfig) *http.Client { | ||
| transport := http.DefaultTransport.(*http.Transport).Clone() | ||
| transport.Proxy = h.getProxyFunc(routingConfig) | ||
| transport.TLSClientConfig = &tls.Config{ | ||
| InsecureSkipVerify: true, | ||
| } | ||
|
|
||
| globalConfig := config.GetGlobalConfig() | ||
| return &http.Client{ | ||
| Transport: transport, | ||
| Timeout: 500 * time.Millisecond, | ||
| } | ||
| } | ||
|
|
||
| if globalConfig.Routing != nil && globalConfig.Routing.ProxyConfig != nil { | ||
| func (h *DefaultHttpsClientHolder) getProxyFunc(routingConfig *controller.RoutingConfig) func(*http.Request) (*url.URL, error) { | ||
| if routingConfig != nil && routingConfig.ProxyConfig != nil { | ||
| proxyConf := httpproxy.Config{} | ||
| if globalConfig.Routing.ProxyConfig.HttpProxy != nil { | ||
| proxyConf.HTTPProxy = *globalConfig.Routing.ProxyConfig.HttpProxy | ||
| if routingConfig.ProxyConfig.HttpProxy != nil { | ||
| proxyConf.HTTPProxy = *routingConfig.ProxyConfig.HttpProxy | ||
| } | ||
| if globalConfig.Routing.ProxyConfig.HttpsProxy != nil { | ||
| proxyConf.HTTPSProxy = *globalConfig.Routing.ProxyConfig.HttpsProxy | ||
| if routingConfig.ProxyConfig.HttpsProxy != nil { | ||
| proxyConf.HTTPSProxy = *routingConfig.ProxyConfig.HttpsProxy | ||
| } | ||
| if globalConfig.Routing.ProxyConfig.NoProxy != nil { | ||
| proxyConf.NoProxy = *globalConfig.Routing.ProxyConfig.NoProxy | ||
| if routingConfig.ProxyConfig.NoProxy != nil { | ||
| proxyConf.NoProxy = *routingConfig.ProxyConfig.NoProxy | ||
| } | ||
|
|
||
| proxyFunc := func(req *http.Request) (*url.URL, error) { | ||
| return func(req *http.Request) (*url.URL, error) { | ||
| return proxyConf.ProxyFunc()(req.URL) | ||
| } | ||
| transport.Proxy = proxyFunc | ||
| healthCheckTransport.Proxy = proxyFunc | ||
| } | ||
|
|
||
| httpClient = &http.Client{ | ||
| Transport: transport, | ||
| } | ||
| healthCheckHttpClient = &http.Client{ | ||
| Transport: healthCheckTransport, | ||
| Timeout: 500 * time.Millisecond, | ||
| } | ||
| InjectCertificates(k8s, logger) | ||
| return nil | ||
| } | ||
|
|
||
| func InjectCertificates(k8s client.Client, logger logr.Logger) { | ||
| if certs, ok := readCertificates(k8s, logger); ok { | ||
| func (h *DefaultHttpsClientHolder) getCaCertPool(routingConfig *controller.RoutingConfig) *x509.CertPool { | ||
| if certs, ok := h.readCertificates(routingConfig); ok { | ||
| var caCertPool *x509.CertPool | ||
|
|
||
| systemCertPool, err := x509.SystemCertPool() | ||
| if err != nil { | ||
| h.logger.Error(err, "Failed to load system cert pool") | ||
| caCertPool = x509.NewCertPool() | ||
| } else { | ||
| caCertPool = systemCertPool | ||
| } | ||
|
|
||
| for _, certsPem := range certs { | ||
| injectCertificates([]byte(certsPem), httpClient.Transport.(*http.Transport), logger) | ||
| caCertPool.AppendCertsFromPEM([]byte(certsPem)) | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| return caCertPool | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func readCertificates(k8s client.Client, logger logr.Logger) (map[string]string, bool) { | ||
| configmapRef := config.GetGlobalConfig().Routing.TLSCertificateConfigmapRef | ||
| if configmapRef == nil { | ||
| func (h *DefaultHttpsClientHolder) readCertificates(routingConfig *controller.RoutingConfig) (map[string]string, bool) { | ||
| if routingConfig == nil || routingConfig.TLSCertificateConfigmapRef == nil { | ||
| return nil, false | ||
| } | ||
| configMap := &corev1.ConfigMap{} | ||
| namespacedName := &types.NamespacedName{ | ||
|
|
||
| configmapRef := routingConfig.TLSCertificateConfigmapRef | ||
|
|
||
| namespacedName := types.NamespacedName{ | ||
| Name: configmapRef.Name, | ||
| Namespace: configmapRef.Namespace, | ||
| } | ||
| err := k8s.Get(context.Background(), *namespacedName, configMap) | ||
|
|
||
| configMap := &corev1.ConfigMap{} | ||
| err := h.k8s.Get(context.Background(), namespacedName, configMap) | ||
| if err != nil { | ||
| logger.Error(err, "Failed to read configmap with certificates") | ||
| h.logger.Error(err, "Failed to read configmap with certificates") | ||
| return nil, false | ||
| } | ||
| return configMap.Data, true | ||
| } | ||
|
|
||
| func injectCertificates(certsPem []byte, transport *http.Transport, logger logr.Logger) { | ||
| caCertPool := transport.TLSClientConfig.RootCAs | ||
| if caCertPool == nil { | ||
| systemCertPool, err := x509.SystemCertPool() | ||
| if err != nil { | ||
| logger.Error(err, "Failed to load system cert pool") | ||
| caCertPool = x509.NewCertPool() | ||
| } else { | ||
| caCertPool = systemCertPool | ||
| } | ||
| } | ||
| if ok := caCertPool.AppendCertsFromPEM(certsPem); ok { | ||
| transport.TLSClientConfig = &tls.Config{RootCAs: caCertPool} | ||
| } | ||
| return configMap.Data, true | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.