@@ -18,8 +18,10 @@ package main
1818
1919import (
2020 "context"
21+ "crypto/tls"
2122 "flag"
2223 "fmt"
24+ "log"
2325 "net/http"
2426 "os"
2527 "path/filepath"
@@ -41,9 +43,11 @@ import (
4143 "k8s.io/klog/v2/textlogger"
4244 ctrl "sigs.k8s.io/controller-runtime"
4345 crcache "sigs.k8s.io/controller-runtime/pkg/cache"
46+ "sigs.k8s.io/controller-runtime/pkg/certwatcher"
4447 "sigs.k8s.io/controller-runtime/pkg/client"
4548 crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer"
4649 "sigs.k8s.io/controller-runtime/pkg/healthz"
50+ "sigs.k8s.io/controller-runtime/pkg/metrics/filters"
4751 "sigs.k8s.io/controller-runtime/pkg/metrics/server"
4852
4953 catalogd "github.com/operator-framework/catalogd/api/v1"
@@ -70,6 +74,7 @@ import (
7074var (
7175 setupLog = ctrl .Log .WithName ("setup" )
7276 defaultSystemNamespace = "olmv1-system"
77+ certWatcher * certwatcher.CertWatcher
7378)
7479
7580const authFilePrefix = "operator-controller-global-pull-secrets"
@@ -89,17 +94,25 @@ func podNamespace() string {
8994func main () {
9095 var (
9196 metricsAddr string
97+ certFile string
98+ keyFile string
9299 enableLeaderElection bool
100+ disableMetricsTLS bool
93101 probeAddr string
94102 cachePath string
95103 operatorControllerVersion bool
96104 systemNamespace string
97105 caCertDir string
98106 globalPullSecret string
99107 )
100- flag .StringVar (& metricsAddr , "metrics-bind-address" , ":8080" , "The address the metric endpoint binds to." )
108+ flag .StringVar (& metricsAddr , "metrics-bind-address" , "0" , "The address the metrics endpoint binds to. " +
109+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service." )
101110 flag .StringVar (& probeAddr , "health-probe-bind-address" , ":8081" , "The address the probe endpoint binds to." )
102111 flag .StringVar (& caCertDir , "ca-certs-dir" , "" , "The directory of TLS certificate to use for verifying HTTPS connections to the Catalogd and docker-registry web servers." )
112+ flag .BoolVar (& disableMetricsTLS , "disable-metrics-tls" , false ,
113+ "Disables TLS for the metrics endpoint, making it insecure. Use with caution. Defaults to secure (false)." )
114+ flag .StringVar (& certFile , "tls-cert" , "" , "The certificate file used for serving metrics contents over HTTPS. Requires tls-key." )
115+ flag .StringVar (& keyFile , "tls-key" , "" , "The key file used for serving metrics contents over HTTPS. Requires tls-cert." )
103116 flag .BoolVar (& enableLeaderElection , "leader-elect" , false ,
104117 "Enable leader election for controller manager. " +
105118 "Enabling this will ensure there is only one active controller manager." )
@@ -119,6 +132,11 @@ func main() {
119132 os .Exit (0 )
120133 }
121134
135+ if (certFile != "" && keyFile == "" ) || (certFile == "" && keyFile != "" ) {
136+ setupLog .Error (nil , "unable to configure TLS certificates: tls-cert and tls-key flags must be used together" )
137+ os .Exit (1 )
138+ }
139+
122140 ctrl .SetLogger (textlogger .NewLogger (textlogger .NewConfig ()))
123141
124142 setupLog .Info ("starting up the controller" , "version info" , version .String ())
@@ -161,9 +179,46 @@ func main() {
161179 },
162180 }
163181 }
182+
183+ metricsServerOptions := server.Options {
184+ BindAddress : metricsAddr ,
185+ SecureServing : ! disableMetricsTLS ,
186+ }
187+
188+ if ! disableMetricsTLS {
189+ setupLog .Info ("Metrics endpoint will be protected by TLS." )
190+
191+ // FilterProvider is used to protect the metrics endpoint with authn/authz.
192+ // These configurations ensure that only authorized users and service accounts
193+ // can access the metrics endpoint.
194+ metricsServerOptions .FilterProvider = filters .WithAuthenticationAndAuthorization
195+
196+ // If the certificate files are provided, the metrics server will use them to serve the metrics endpoint.
197+ // Otherwise, the metrics server will use the default certificate provided by the controller-runtime which
198+ // is not recommended for production use.
199+ if len (certFile ) > 0 && len (keyFile ) > 0 {
200+ setupLog .Info ("Using provided TLS certificate and key files for the metrics server." ,
201+ "certFile" , certFile , "keyFile" , keyFile )
202+
203+ // If the certificate files change, the watcher will reload them.
204+ var err error
205+ certWatcher , err = certwatcher .New (certFile , keyFile )
206+ if err != nil {
207+ log .Fatalf ("Failed to initialize certificate watcher: %v" , err )
208+ }
209+ metricsServerOptions .TLSOpts = append (metricsServerOptions .TLSOpts , func (config * tls.Config ) {
210+ config .GetCertificate = certWatcher .GetCertificate
211+ })
212+ } else {
213+ setupLog .Info ("WARNING: No certificate files provided. (not recommended for production)." )
214+ }
215+ } else {
216+ setupLog .Info ("WARNING: Metrics endpoint is not protected by TLS. Ensure this is intentional." )
217+ }
218+
164219 mgr , err := ctrl .NewManager (ctrl .GetConfigOrDie (), ctrl.Options {
165220 Scheme : scheme .Scheme ,
166- Metrics : server. Options { BindAddress : metricsAddr } ,
221+ Metrics : metricsServerOptions ,
167222 HealthProbeBindAddress : probeAddr ,
168223 LeaderElection : enableLeaderElection ,
169224 LeaderElectionID : "9c4404e7.operatorframework.io" ,
@@ -337,6 +392,14 @@ func main() {
337392
338393 //+kubebuilder:scaffold:builder
339394
395+ if ! disableMetricsTLS && certWatcher != nil {
396+ setupLog .Info ("Adding certificate watcher to manager" )
397+ if err := mgr .Add (certWatcher ); err != nil {
398+ setupLog .Error (err , "unable to add certificate watcher to manager" )
399+ os .Exit (1 )
400+ }
401+ }
402+
340403 if err := mgr .AddHealthzCheck ("healthz" , healthz .Ping ); err != nil {
341404 setupLog .Error (err , "unable to set up health check" )
342405 os .Exit (1 )
0 commit comments