Skip to content

Commit 6a7af10

Browse files
committed
Inject certificate to http client from a configmap referenced in the config
Signed-off-by: ivinokur <[email protected]>
1 parent 04f5369 commit 6a7af10

File tree

9 files changed

+111
-6
lines changed

9 files changed

+111
-6
lines changed

apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ type RoutingConfig struct {
6161
// DevWorkspaces. However, changing the proxy configuration for the DevWorkspace Operator itself
6262
// requires restarting the controller deployment.
6363
ProxyConfig *Proxy `json:"proxyConfig,omitempty"`
64+
// TLSCertificateConfigmapRef defines the name and namespace of the configmap with a certificate to inject to http
65+
// client.
66+
TLSCertificateConfigmapRef *ConfigmapReference `json:"tlsCertificateConfigmapRef,omitempty"`
6467
}
6568

6669
type WorkspaceConfig struct {
@@ -240,6 +243,13 @@ type ProjectCloneConfig struct {
240243
Env []corev1.EnvVar `json:"env,omitempty"`
241244
}
242245

246+
type ConfigmapReference struct {
247+
// Name is the name of the configmap
248+
Name string `json:"name"`
249+
// Namespace is the namespace of the configmap
250+
Namespace string `json:"namespace"`
251+
}
252+
243253
// DevWorkspaceOperatorConfig is the Schema for the devworkspaceoperatorconfigs API
244254
// +kubebuilder:object:root=true
245255
// +kubebuilder:resource:path=devworkspaceoperatorconfigs,scope=Namespaced,shortName=dwoc

controllers/workspace/devworkspace_controller.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -667,9 +667,8 @@ func (r *DevWorkspaceReconciler) getWorkspaceId(ctx context.Context, workspace *
667667
}
668668
}
669669

670-
func (r *DevWorkspaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
671-
setupHttpClients()
672-
670+
func (r *DevWorkspaceReconciler) SetupWithManager(mgr ctrl.Manager, k8s client.Client) error {
671+
setupHttpClients(k8s)
673672
maxConcurrentReconciles, err := wkspConfig.GetMaxConcurrentReconciles()
674673
if err != nil {
675674
return err
@@ -681,6 +680,7 @@ func (r *DevWorkspaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
681680

682681
configWatcher := builder.WithPredicates(wkspConfig.Predicates())
683682
automountWatcher := builder.WithPredicates(automountPredicates)
683+
certificateWatcher := builder.WithPredicates(certificatePredicates)
684684

685685
// TODO: Set up indexing https://book.kubebuilder.io/cronjob-tutorial/controller-implementation.html#setup
686686
return ctrl.NewControllerManagedBy(mgr).
@@ -700,6 +700,7 @@ func (r *DevWorkspaceReconciler) SetupWithManager(mgr ctrl.Manager) error {
700700
Watches(&source.Kind{Type: &corev1.PersistentVolumeClaim{}}, handler.EnqueueRequestsFromMapFunc(r.dwPVCHandler)).
701701
Watches(&source.Kind{Type: &corev1.Secret{}}, handler.EnqueueRequestsFromMapFunc(r.runningWorkspacesHandler), automountWatcher).
702702
Watches(&source.Kind{Type: &corev1.ConfigMap{}}, handler.EnqueueRequestsFromMapFunc(r.runningWorkspacesHandler), automountWatcher).
703+
Watches(&source.Kind{Type: &corev1.ConfigMap{}}, handler.EnqueueRequestsFromMapFunc(r.certificateHandler), certificateWatcher).
703704
Watches(&source.Kind{Type: &corev1.PersistentVolumeClaim{}}, handler.EnqueueRequestsFromMapFunc(r.runningWorkspacesHandler), automountWatcher).
704705
Watches(&source.Kind{Type: &controllerv1alpha1.DevWorkspaceOperatorConfig{}}, handler.EnqueueRequestsFromMapFunc(emptyMapper), configWatcher).
705706
WithEventFilter(devworkspacePredicates).

controllers/workspace/eventhandlers.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ package controllers
1515

1616
import (
1717
"context"
18+
corev1 "k8s.io/api/core/v1"
19+
"net/http"
1820

1921
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
2022
wkspConfig "github.com/devfile/devworkspace-operator/pkg/config"
@@ -106,3 +108,11 @@ func (r *DevWorkspaceReconciler) runningWorkspacesHandler(obj client.Object) []r
106108
}
107109
return reconciles
108110
}
111+
112+
func (r *DevWorkspaceReconciler) certificateHandler(obj client.Object) []reconcile.Request {
113+
certsPem, ok := obj.(*corev1.ConfigMap).Data["custom-ca-certificates.pem"]
114+
if ok {
115+
injectCertificates([]byte(certsPem), httpClient.Transport.(*http.Transport))
116+
}
117+
return []reconcile.Request{}
118+
}

controllers/workspace/http.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@
1414
package controllers
1515

1616
import (
17+
"context"
1718
"crypto/tls"
19+
"crypto/x509"
20+
corev1 "k8s.io/api/core/v1"
21+
"k8s.io/apimachinery/pkg/types"
1822
"net/http"
1923
"net/url"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
2025
"time"
2126

2227
"github.com/devfile/devworkspace-operator/pkg/config"
@@ -28,8 +33,12 @@ var (
2833
healthCheckHttpClient *http.Client
2934
)
3035

31-
func setupHttpClients() {
36+
func setupHttpClients(k8s client.Client) {
3237
transport := http.DefaultTransport.(*http.Transport).Clone()
38+
certs, ok := readCertificates(k8s)
39+
if ok {
40+
injectCertificates(certs, transport)
41+
}
3342
healthCheckTransport := http.DefaultTransport.(*http.Transport).Clone()
3443
healthCheckTransport.TLSClientConfig = &tls.Config{
3544
InsecureSkipVerify: true,
@@ -64,3 +73,31 @@ func setupHttpClients() {
6473
Timeout: 500 * time.Millisecond,
6574
}
6675
}
76+
77+
func readCertificates(k8s client.Client) ([]byte, bool) {
78+
configmapRef := config.GetGlobalConfig().Routing.TLSCertificateConfigmapRef
79+
if configmapRef == nil {
80+
return nil, false
81+
}
82+
configMap := &corev1.ConfigMap{}
83+
namespacedName := &types.NamespacedName{
84+
Name: configmapRef.Name,
85+
Namespace: configmapRef.Namespace,
86+
}
87+
err := k8s.Get(context.Background(), *namespacedName, configMap)
88+
if err == nil {
89+
certificates, ok := configMap.Data["custom-ca-certificates.pem"]
90+
if ok {
91+
return []byte(certificates), true
92+
}
93+
}
94+
return []byte{}, false
95+
}
96+
97+
func injectCertificates(certsPem []byte, transport *http.Transport) {
98+
caCertPool := x509.NewCertPool()
99+
ok := caCertPool.AppendCertsFromPEM(certsPem)
100+
if ok {
101+
transport.TLSClientConfig = &tls.Config{RootCAs: caCertPool}
102+
}
103+
}

controllers/workspace/predicates.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package controllers
1717

1818
import (
1919
dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
20+
"github.com/devfile/devworkspace-operator/pkg/config"
2021
"github.com/devfile/devworkspace-operator/pkg/constants"
2122

2223
corev1 "k8s.io/api/core/v1"
@@ -99,6 +100,19 @@ var automountPredicates = predicate.Funcs{
99100
GenericFunc: func(_ event.GenericEvent) bool { return false },
100101
}
101102

103+
var certificatePredicates = predicate.Funcs{
104+
CreateFunc: func(ev event.CreateEvent) bool {
105+
return objectIsCertificateConfigmap(ev.Object)
106+
},
107+
DeleteFunc: func(ev event.DeleteEvent) bool {
108+
return objectIsCertificateConfigmap(ev.Object)
109+
},
110+
UpdateFunc: func(ev event.UpdateEvent) bool {
111+
return objectIsCertificateConfigmap(ev.ObjectNew)
112+
},
113+
GenericFunc: func(_ event.GenericEvent) bool { return false },
114+
}
115+
102116
func objectIsAutomountResource(obj client.Object) bool {
103117
labels := obj.GetLabels()
104118
switch {
@@ -112,3 +126,12 @@ func objectIsAutomountResource(obj client.Object) bool {
112126
}
113127

114128
}
129+
130+
func objectIsCertificateConfigmap(obj client.Object) bool {
131+
configmapRef := config.GetGlobalConfig().Routing.TLSCertificateConfigmapRef
132+
if configmapRef == nil {
133+
return false
134+
} else {
135+
return configmapRef.Name == obj.GetName() && configmapRef.Namespace == obj.GetNamespace()
136+
}
137+
}

controllers/workspace/suite_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ var _ = BeforeSuite(func() {
138138
NonCachingClient: nonCachingClient,
139139
Log: ctrl.Log.WithName("controllers").WithName("DevWorkspace"),
140140
Scheme: mgr.GetScheme(),
141-
}).SetupWithManager(mgr)
141+
}).SetupWithManager(mgr, nonCachingClient)
142142
Expect(err).NotTo(HaveOccurred())
143143

144144
// Set HTTP client to fail all requests by default; tests that require HTTP must set this up directly

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func main() {
165165
NonCachingClient: nonCachingClient,
166166
Log: ctrl.Log.WithName("controllers").WithName("DevWorkspace"),
167167
Scheme: mgr.GetScheme(),
168-
}).SetupWithManager(mgr); err != nil {
168+
}).SetupWithManager(mgr, nonCachingClient); err != nil {
169169
setupLog.Error(err, "unable to create controller", "controller", "DevWorkspace")
170170
os.Exit(1)
171171
}

pkg/config/proxy/openshift.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ func GetClusterProxyConfig(nonCachedClient crclient.Client) (*controller.Proxy,
5959
return proxyConfig, nil
6060
}
6161

62+
// MergeTLSCertificateConfigmapRef merges tls certificate configmap reference configurations
63+
// from the operator and the cluster and merges them, with the operator configuration taking precedence.
64+
func MergeTLSCertificateConfigmapRef(operatorConfig, clusterConfig *controller.ConfigmapReference) *controller.ConfigmapReference {
65+
mergedConfigmapReference := &controller.ConfigmapReference{
66+
Name: operatorConfig.Name,
67+
Namespace: operatorConfig.Namespace,
68+
}
69+
70+
if mergedConfigmapReference.Name == "" {
71+
mergedConfigmapReference.Name = clusterConfig.Name
72+
}
73+
if mergedConfigmapReference.Namespace == "" {
74+
mergedConfigmapReference.Namespace = clusterConfig.Namespace
75+
}
76+
77+
return mergedConfigmapReference
78+
}
79+
6280
// MergeProxyConfigs merges proxy configurations from the operator and the cluster and merges them, with the
6381
// operator configuration taking precedence. Accepts nil arguments. If both arguments are nil, returns nil.
6482
func MergeProxyConfigs(operatorConfig, clusterConfig *controller.Proxy) *controller.Proxy {

pkg/config/sync.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@ func mergeConfig(from, to *controller.OperatorConfiguration) {
269269
}
270270
to.Routing.ProxyConfig = proxy.MergeProxyConfigs(from.Routing.ProxyConfig, defaultConfig.Routing.ProxyConfig)
271271
}
272+
if from.Routing.TLSCertificateConfigmapRef != nil {
273+
if to.Routing.TLSCertificateConfigmapRef == nil {
274+
to.Routing.TLSCertificateConfigmapRef = &controller.ConfigmapReference{}
275+
}
276+
to.Routing.TLSCertificateConfigmapRef = proxy.MergeTLSCertificateConfigmapRef(from.Routing.TLSCertificateConfigmapRef, defaultConfig.Routing.TLSCertificateConfigmapRef)
277+
}
272278
}
273279
if from.Workspace != nil {
274280
if to.Workspace == nil {

0 commit comments

Comments
 (0)