Skip to content

Commit c23bdcc

Browse files
committed
adding trusted-ca-bundle manager
Signed-off-by: Evgeny Slutsky <eslutsky@redhat.com>
1 parent 8f0d7d6 commit c23bdcc

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

pkg/cmd/run.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ func RunMicroshift(cfg *config.Config) error {
236236
util.Must(m.AddService(controllers.NewClusterID(cfg)))
237237
util.Must(m.AddService(controllers.NewTelemetryManager(cfg)))
238238
util.Must(m.AddService(controllers.NewHostsWatcherManager(cfg)))
239+
util.Must(m.AddService(controllers.NewTrustedCABundleManager(cfg)))
239240
util.Must(m.AddService(gdp.NewGenericDevicePlugin(cfg)))
240241

241242
// Storing and clearing the env, so other components don't send the READY=1 until MicroShift is fully ready
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package controllers
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
"time"
10+
11+
"github.com/fsnotify/fsnotify"
12+
"github.com/openshift/microshift/pkg/config"
13+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
"k8s.io/client-go/kubernetes"
15+
"k8s.io/client-go/tools/clientcmd"
16+
"k8s.io/klog/v2"
17+
)
18+
19+
const (
20+
trustedCABundlePath = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"
21+
trustedCABundleLabel = "config.openshift.io/inject-trusted-cabundle"
22+
trustedCABundleKey = "ca-bundle.crt"
23+
resyncInterval = 30 * time.Second
24+
)
25+
26+
type TrustedCABundleManager struct {
27+
kubeconfig string
28+
}
29+
30+
func NewTrustedCABundleManager(cfg *config.Config) *TrustedCABundleManager {
31+
return &TrustedCABundleManager{
32+
kubeconfig: cfg.KubeConfigPath(config.KubeAdmin),
33+
}
34+
}
35+
36+
func (s *TrustedCABundleManager) Name() string { return "trusted-ca-bundle-manager" }
37+
func (s *TrustedCABundleManager) Dependencies() []string {
38+
return []string{"kube-apiserver", "infrastructure-services-manager"}
39+
}
40+
41+
func (s *TrustedCABundleManager) Run(ctx context.Context, ready chan<- struct{}, stopped chan<- struct{}) error {
42+
defer close(stopped)
43+
44+
restCfg, err := clientcmd.BuildConfigFromFlags("", s.kubeconfig)
45+
if err != nil {
46+
return fmt.Errorf("failed to build kubeconfig: %w", err)
47+
}
48+
client, err := kubernetes.NewForConfig(restCfg)
49+
if err != nil {
50+
return fmt.Errorf("failed to create Kubernetes client: %w", err)
51+
}
52+
53+
if err := s.syncTrustedCABundle(ctx, client); err != nil {
54+
klog.Warningf("%s failed initial trusted CA bundle sync: %v", s.Name(), err)
55+
}
56+
57+
watcher, err := fsnotify.NewWatcher()
58+
if err != nil {
59+
return fmt.Errorf("failed to create file watcher: %w", err)
60+
}
61+
defer func() {
62+
if cerr := watcher.Close(); cerr != nil {
63+
klog.Errorf("%s failed to close file watcher: %v", s.Name(), cerr)
64+
}
65+
}()
66+
67+
for _, path := range []string{trustedCABundlePath, filepath.Dir(trustedCABundlePath)} {
68+
if err := watcher.Add(path); err != nil {
69+
klog.Warningf("%s failed to watch %s: %v", s.Name(), path, err)
70+
}
71+
}
72+
73+
close(ready)
74+
klog.Infof("%s is ready, watching %s", s.Name(), trustedCABundlePath)
75+
76+
lastHash, _ := s.getFileHash()
77+
resyncTicker := time.NewTicker(resyncInterval)
78+
defer resyncTicker.Stop()
79+
80+
for {
81+
select {
82+
case <-ctx.Done():
83+
return ctx.Err()
84+
85+
case <-resyncTicker.C:
86+
if err := s.syncTrustedCABundle(ctx, client); err != nil {
87+
klog.V(4).Infof("%s periodic sync: %v", s.Name(), err)
88+
}
89+
90+
case event, ok := <-watcher.Events:
91+
if !ok {
92+
return fmt.Errorf("watcher channel closed")
93+
}
94+
if event.Name != trustedCABundlePath {
95+
continue
96+
}
97+
if event.Op&fsnotify.Write == 0 && event.Op&fsnotify.Create == 0 {
98+
continue
99+
}
100+
currentHash, err := s.getFileHash()
101+
if err != nil {
102+
klog.Warningf("%s failed to hash CA bundle: %v", s.Name(), err)
103+
continue
104+
}
105+
if currentHash == lastHash {
106+
continue
107+
}
108+
klog.Infof("%s detected CA bundle change, syncing", s.Name())
109+
if err := s.syncTrustedCABundle(ctx, client); err != nil {
110+
klog.Errorf("%s failed to sync CA bundle: %v", s.Name(), err)
111+
continue
112+
}
113+
lastHash = currentHash
114+
115+
case err, ok := <-watcher.Errors:
116+
if !ok {
117+
return fmt.Errorf("watcher error channel closed")
118+
}
119+
klog.Errorf("%s watcher error: %v", s.Name(), err)
120+
}
121+
}
122+
}
123+
124+
func (s *TrustedCABundleManager) syncTrustedCABundle(ctx context.Context, client kubernetes.Interface) error {
125+
caBundle, err := os.ReadFile(trustedCABundlePath)
126+
if err != nil {
127+
return fmt.Errorf("failed to read %s: %w", trustedCABundlePath, err)
128+
}
129+
if len(caBundle) == 0 {
130+
return fmt.Errorf("%s is empty", trustedCABundlePath)
131+
}
132+
133+
configMaps, err := client.CoreV1().ConfigMaps("").List(ctx, metav1.ListOptions{
134+
LabelSelector: trustedCABundleLabel + "=true",
135+
})
136+
if err != nil {
137+
return fmt.Errorf("failed to list ConfigMaps with label %s: %w", trustedCABundleLabel, err)
138+
}
139+
140+
caBundleStr := string(caBundle)
141+
for i := range configMaps.Items {
142+
cm := &configMaps.Items[i]
143+
if cm.Data == nil {
144+
cm.Data = map[string]string{}
145+
}
146+
if cm.Data[trustedCABundleKey] == caBundleStr {
147+
klog.V(4).Infof("%s ConfigMap %s/%s already has current CA bundle", s.Name(), cm.Namespace, cm.Name)
148+
continue
149+
}
150+
cm.Data[trustedCABundleKey] = caBundleStr
151+
if _, err := client.CoreV1().ConfigMaps(cm.Namespace).Update(ctx, cm, metav1.UpdateOptions{}); err != nil {
152+
klog.Errorf("%s failed to update ConfigMap %s/%s: %v", s.Name(), cm.Namespace, cm.Name, err)
153+
continue
154+
}
155+
klog.Infof("%s injected trusted CA bundle into ConfigMap %s/%s", s.Name(), cm.Namespace, cm.Name)
156+
}
157+
158+
return nil
159+
}
160+
161+
func (s *TrustedCABundleManager) getFileHash() (string, error) {
162+
content, err := os.ReadFile(trustedCABundlePath)
163+
if err != nil {
164+
return "", err
165+
}
166+
hash := sha256.Sum256(content)
167+
return fmt.Sprintf("%x", hash), nil
168+
}

0 commit comments

Comments
 (0)