Skip to content

Commit 410f70c

Browse files
committed
ACM-30179: Use TLS configuration from the APIServer when available
OCP 4.22 indroduces a new feature in the APIServer for configuring TLS configuration, and we want our components to also use the same TLS configuration that is defined in the APIServer. When starting a components we will attempt to get the APIServer and check its TLSAdherencePolicy (decides whether we should apply the TLS configuration) and TLSProfileSpec (the actual TLS configuration). For our operator we also add a controller called SecurityProfileWatcher (provided by openshift/controller-runtime-common) in order to force a restart when these values change. For our server we implement a similar behavior ourselves. This also required updating a lot of packages including the client-go which further required setting the environment variable KUBE_FEATURE_WatchListClient to false on the operator in order to fix a known bug where controllers don't start due to a cache sync issue.
1 parent 99a951c commit 410f70c

6,036 files changed

Lines changed: 689274 additions & 752778 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
annotations:
5+
service.beta.openshift.io/serving-cert-secret-name: ibi-metrics-serving-certs
6+
creationTimestamp: null
7+
name: image-based-install-metrics
8+
spec:
9+
ports:
10+
- port: 8080
11+
protocol: TCP
12+
targetPort: 8080
13+
selector:
14+
app: image-based-install-operator
15+
status:
16+
loadBalancer: {}

bundle/manifests/image-based-install-operator.clusterserviceversion.yaml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ metadata:
3636
}
3737
]
3838
capabilities: Basic Install
39-
createdAt: "2026-02-05T10:59:19Z"
39+
createdAt: "2026-04-20T07:31:40Z"
4040
operators.operatorframework.io/builder: operator-sdk-v1.30.0
4141
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
4242
name: image-based-install-operator.v0.0.1
@@ -82,6 +82,26 @@ spec:
8282
- patch
8383
- update
8484
- watch
85+
- apiGroups:
86+
- authentication.k8s.io
87+
resources:
88+
- tokenreviews
89+
verbs:
90+
- create
91+
- apiGroups:
92+
- authorization.k8s.io
93+
resources:
94+
- subjectaccessreviews
95+
verbs:
96+
- create
97+
- apiGroups:
98+
- config.openshift.io
99+
resources:
100+
- apiservers
101+
verbs:
102+
- get
103+
- list
104+
- watch
85105
- apiGroups:
86106
- extensions.hive.openshift.io
87107
resources:
@@ -196,6 +216,8 @@ spec:
196216
value: "1"
197217
- name: TMPDIR
198218
value: /data
219+
- name: KUBE_FEATURE_WatchListClient
220+
value: "false"
199221
image: controller:latest
200222
livenessProbe:
201223
httpGet:
@@ -226,6 +248,8 @@ spec:
226248
name: data
227249
- mountPath: /webhook-certs
228250
name: webhook-certs
251+
- mountPath: /metrics-certs
252+
name: metrics-certs
229253
- command:
230254
- /usr/local/bin/server
231255
env:
@@ -279,6 +303,9 @@ spec:
279303
- name: webhook-certs
280304
secret:
281305
secretName: ibi-webhook-serving-certs
306+
- name: metrics-certs
307+
secret:
308+
secretName: ibi-metrics-serving-certs
282309
permissions:
283310
- rules:
284311
- apiGroups:

cmd/manager/main.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package main
1818

1919
import (
2020
"context"
21+
"crypto/tls"
2122
"flag"
2223
"fmt"
2324
"net/http"
@@ -26,11 +27,13 @@ import (
2627
"os"
2728
"time"
2829

30+
crtls "github.com/openshift/controller-runtime-common/pkg/tls"
2931
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
3032
// to ensure that exec-entrypoint and run can make use of them.
3133
_ "k8s.io/client-go/plugin/pkg/client/auth"
3234
"sigs.k8s.io/controller-runtime/pkg/manager"
3335

36+
configv1 "github.com/openshift/api/config/v1"
3437
corev1 "k8s.io/api/core/v1"
3538
"k8s.io/apimachinery/pkg/labels"
3639
"k8s.io/apimachinery/pkg/runtime"
@@ -41,6 +44,7 @@ import (
4144
"sigs.k8s.io/controller-runtime/pkg/client"
4245
"sigs.k8s.io/controller-runtime/pkg/healthz"
4346
"sigs.k8s.io/controller-runtime/pkg/log/zap"
47+
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
4448
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
4549
"sigs.k8s.io/controller-runtime/pkg/webhook"
4650

@@ -54,6 +58,7 @@ import (
5458
"github.com/openshift/image-based-install-operator/internal/credentials"
5559
"github.com/openshift/image-based-install-operator/internal/installer"
5660
"github.com/openshift/image-based-install-operator/internal/monitor"
61+
"github.com/openshift/image-based-install-operator/internal/tlsconfig"
5762
//+kubebuilder:scaffold:imports
5863
)
5964

@@ -67,6 +72,7 @@ func init() {
6772
utilruntime.Must(v1alpha1.AddToScheme(scheme))
6873
utilruntime.Must(bmh_v1alpha1.AddToScheme(scheme))
6974
utilruntime.Must(hivev1.AddToScheme(scheme))
75+
utilruntime.Must(configv1.AddToScheme(scheme))
7076
//+kubebuilder:scaffold:scheme
7177
}
7278

@@ -96,17 +102,32 @@ func main() {
96102
go startPPROF(logger)
97103
}
98104

99-
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
105+
ctx, cancel := context.WithCancel(ctrl.SetupSignalHandler())
106+
defer cancel()
107+
108+
restCfg := ctrl.GetConfigOrDie()
109+
tlsResult, err := tlsconfig.ResolveTLSConfig(context.Background(), restCfg)
110+
if err != nil {
111+
setupLog.Error(err, "unable to resolve TLS config")
112+
os.Exit(1)
113+
}
114+
115+
mgr, err := ctrl.NewManager(restCfg, ctrl.Options{
100116
Scheme: scheme,
101117
Metrics: metricsserver.Options{
102-
BindAddress: metricsAddr,
118+
BindAddress: metricsAddr,
119+
SecureServing: true,
120+
CertDir: "/metrics-certs",
121+
FilterProvider: filters.WithAuthenticationAndAuthorization,
122+
TLSOpts: []func(*tls.Config){tlsResult.TLSConfig},
103123
},
104124
HealthProbeBindAddress: probeAddr,
105125
LeaderElection: enableLeaderElection,
106126
LeaderElectionID: "e21b2704.openshift.io",
107127
WebhookServer: webhook.NewServer(webhook.Options{
108128
Port: 9443,
109129
CertDir: "/webhook-certs",
130+
TLSOpts: []func(*tls.Config){tlsResult.TLSConfig},
110131
}),
111132
Cache: cache.Options{
112133
ByObject: map[client.Object]cache.ByObject{
@@ -179,6 +200,25 @@ func main() {
179200
os.Exit(1)
180201
}
181202

203+
if err := (&crtls.SecurityProfileWatcher{
204+
Client: mgr.GetClient(),
205+
InitialTLSAdherencePolicy: tlsResult.TLSAdherencePolicy,
206+
InitialTLSProfileSpec: tlsResult.TLSProfileSpec,
207+
OnAdherencePolicyChange: func(_ context.Context, oldPolicy, newPolicy configv1.TLSAdherencePolicy) {
208+
logger.Infof("TLS adherence policy has changed, shutting down to reload, oldPolicy: %v, newPolicy: %v",
209+
oldPolicy, newPolicy)
210+
cancel()
211+
},
212+
OnProfileChange: func(_ context.Context, oldProfile, newProfile configv1.TLSProfileSpec) {
213+
logger.Infof("TLS profile has changed, shutting down to reload, oldProfile: %v, newProfile: %v",
214+
oldProfile, newProfile)
215+
cancel()
216+
},
217+
}).SetupWithManager(mgr); err != nil {
218+
setupLog.Error(err, "unable to create TLS security profile watcher")
219+
os.Exit(1)
220+
}
221+
182222
if err = (&v1alpha1.ImageClusterInstall{}).SetupWebhookWithManager(mgr); err != nil {
183223
setupLog.Error(err, "unable to create webhook", "webhook", "ImageClusterInstall")
184224
os.Exit(1)
@@ -197,7 +237,7 @@ func main() {
197237
go EnqueueExistingImageClusterInstall(mgr)
198238

199239
setupLog.Info("starting manager")
200-
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
240+
if err := mgr.Start(ctx); err != nil {
201241
setupLog.Error(err, "problem running manager")
202242
os.Exit(1)
203243
}

cmd/server/main.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package main
22

33
import (
44
"context"
5+
"crypto/tls"
6+
"flag"
57
"fmt"
68
"net/http"
79
"os"
@@ -11,8 +13,21 @@ import (
1113
"time"
1214

1315
"github.com/kelseyhightower/envconfig"
14-
"github.com/openshift/image-based-install-operator/internal/imageserver"
16+
configv1 "github.com/openshift/api/config/v1"
17+
configclientset "github.com/openshift/client-go/config/clientset/versioned"
18+
crtls "github.com/openshift/controller-runtime-common/pkg/tls"
1519
"github.com/sirupsen/logrus"
20+
"k8s.io/apimachinery/pkg/api/equality"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
"k8s.io/apimachinery/pkg/watch"
23+
24+
"github.com/openshift/image-based-install-operator/internal/imageserver"
25+
"github.com/openshift/image-based-install-operator/internal/tlsconfig"
26+
27+
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
28+
_ "k8s.io/client-go/plugin/pkg/client/auth"
29+
ctrl "sigs.k8s.io/controller-runtime"
30+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
1631
)
1732

1833
var Options struct {
@@ -23,6 +38,13 @@ var Options struct {
2338
}
2439

2540
func main() {
41+
opts := zap.Options{
42+
Development: true,
43+
}
44+
opts.BindFlags(flag.CommandLine)
45+
flag.Parse()
46+
47+
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
2648
log := logrus.New()
2749
log.SetReportCaller(true)
2850

@@ -50,8 +72,23 @@ func main() {
5072
go func() {
5173
var err error
5274
if Options.HTTPSKeyFile != "" && Options.HTTPSCertFile != "" {
75+
var tlsResult tlsconfig.TLSConfigResult
76+
var cert tls.Certificate
5377
log.Infof("Starting https handler on %s...", server.Addr)
54-
err = server.ListenAndServeTLS(Options.HTTPSCertFile, Options.HTTPSKeyFile)
78+
79+
restCfg := ctrl.GetConfigOrDie()
80+
tlsResult, err = tlsconfig.ResolveTLSConfig(context.Background(), restCfg)
81+
if err != nil {
82+
log.WithError(err).Fatal("unable to configure HTTPS TLS")
83+
}
84+
go watchAndExitOnTLSChange(context.Background(), log, configclientset.NewForConfigOrDie(restCfg), tlsResult)
85+
86+
cert, err = tls.LoadX509KeyPair(Options.HTTPSCertFile, Options.HTTPSKeyFile)
87+
if err != nil {
88+
log.WithError(err).Fatal("failed to load HTTPS certificate")
89+
}
90+
server.TLSConfig = tlsconfig.ServingTLSConfig(tlsResult, cert)
91+
err = server.ListenAndServeTLS("", "")
5592
} else {
5693
log.Infof("Starting http handler on %s...", server.Addr)
5794
err = server.ListenAndServe()
@@ -75,3 +112,43 @@ func main() {
75112
log.Info("server terminated gracefully")
76113
}
77114
}
115+
116+
func watchAndExitOnTLSChange(ctx context.Context, log *logrus.Logger, configClient configclientset.Interface, current tlsconfig.TLSConfigResult) {
117+
w, err := configClient.ConfigV1().APIServers().Watch(ctx, metav1.ListOptions{
118+
FieldSelector: "metadata.name=cluster",
119+
})
120+
if err != nil {
121+
log.WithError(err).Error("failed to watch the APIServer, TLS updates will not be monitored")
122+
return
123+
}
124+
defer w.Stop()
125+
126+
for event := range w.ResultChan() {
127+
if event.Type != watch.Modified {
128+
continue
129+
}
130+
updated, ok := event.Object.(*configv1.APIServer)
131+
if !ok {
132+
continue
133+
}
134+
135+
if current.TLSAdherencePolicy != updated.Spec.TLSAdherence {
136+
log.Infof("TLS adherence policy has changed, shutting down to reload, oldPolicy: %v, newPolicy: %v",
137+
current.TLSAdherencePolicy, updated.Spec.TLSAdherence)
138+
os.Exit(0)
139+
}
140+
141+
profile, err := crtls.GetTLSProfileSpec(updated.Spec.TLSSecurityProfile)
142+
if err != nil {
143+
log.WithError(err).Error("failed to load TLS profile spec after APIServer update, ignoring")
144+
continue
145+
}
146+
if !equality.Semantic.DeepEqual(current.TLSProfileSpec, profile) {
147+
log.Infof("TLS profile has changed, shutting down to reload, oldProfile: %v, newProfile: %v",
148+
current.TLSProfileSpec, profile)
149+
os.Exit(0)
150+
}
151+
}
152+
153+
log.Error("watch on APIServer existed, TLS updates will not be monitored")
154+
}

config/manager/manager.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ spec:
4949
value: "1"
5050
- name: TMPDIR
5151
value: /data
52+
- name: KUBE_FEATURE_WatchListClient
53+
value: "false"
5254
securityContext:
5355
allowPrivilegeEscalation: false
5456
capabilities:
@@ -77,6 +79,8 @@ spec:
7779
mountPath: /data
7880
- name: webhook-certs
7981
mountPath: /webhook-certs
82+
- name: metrics-certs
83+
mountPath: /metrics-certs
8084
- command:
8185
- /usr/local/bin/server
8286
image: controller:latest
@@ -124,6 +128,9 @@ spec:
124128
- name: webhook-certs
125129
secret:
126130
secretName: ibi-webhook-serving-certs
131+
- name: metrics-certs
132+
secret:
133+
secretName: ibi-metrics-serving-certs
127134
serviceAccountName: image-based-install-operator
128135
terminationGracePeriodSeconds: 10
129136
---
@@ -141,3 +148,18 @@ spec:
141148
name: config-server
142149
selector:
143150
app: image-based-install-operator
151+
---
152+
apiVersion: v1
153+
kind: Service
154+
metadata:
155+
name: image-based-install-metrics
156+
namespace: image-based-install-operator
157+
annotations:
158+
service.beta.openshift.io/serving-cert-secret-name: ibi-metrics-serving-certs
159+
spec:
160+
ports:
161+
- port: 8080
162+
protocol: TCP
163+
targetPort: 8080
164+
selector:
165+
app: image-based-install-operator

config/rbac/role.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,26 @@ rules:
2626
- patch
2727
- update
2828
- watch
29+
- apiGroups:
30+
- authentication.k8s.io
31+
resources:
32+
- tokenreviews
33+
verbs:
34+
- create
35+
- apiGroups:
36+
- authorization.k8s.io
37+
resources:
38+
- subjectaccessreviews
39+
verbs:
40+
- create
41+
- apiGroups:
42+
- config.openshift.io
43+
resources:
44+
- apiservers
45+
verbs:
46+
- get
47+
- list
48+
- watch
2949
- apiGroups:
3050
- extensions.hive.openshift.io
3151
resources:

0 commit comments

Comments
 (0)