Skip to content

Commit 955e63b

Browse files
committed
CVO container: Serve HTTP instead of HTTPS
1 parent 0947d3e commit 955e63b

File tree

3 files changed

+11
-217
lines changed

3 files changed

+11
-217
lines changed

cmd/cluster-version-operator/start.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ func init() {
3434
cmd.PersistentFlags().StringVar(&opts.NodeName, "node-name", opts.NodeName, "kubernetes node name CVO is scheduled on.")
3535
cmd.PersistentFlags().BoolVar(&opts.EnableAutoUpdate, "enable-auto-update", opts.EnableAutoUpdate, "Enables the autoupdate controller.")
3636
cmd.PersistentFlags().StringVar(&opts.ReleaseImage, "release-image", opts.ReleaseImage, "The Openshift release image url.")
37-
cmd.PersistentFlags().StringVar(&opts.ServingCertFile, "serving-cert-file", opts.ServingCertFile, "The X.509 certificate file for serving metrics over HTTPS. You must set both --serving-cert-file and --serving-key-file unless you set --listen empty.")
38-
cmd.PersistentFlags().StringVar(&opts.ServingKeyFile, "serving-key-file", opts.ServingKeyFile, "The X.509 key file for serving metrics over HTTPS. You must set both --serving-cert-file and --serving-key-file unless you set --listen empty.")
3937
cmd.PersistentFlags().StringVar(&opts.PromQLTarget.CABundleFile, "metrics-ca-bundle-file", opts.PromQLTarget.CABundleFile, "The service CA bundle file containing one or more X.509 certificate files for validating certificates generated from the service CA for the respective remote PromQL query service.")
4038
cmd.PersistentFlags().StringVar(&opts.PromQLTarget.BearerTokenFile, "metrics-token-file", opts.PromQLTarget.BearerTokenFile, "The bearer token file used to access the remote PromQL query service.")
4139
cmd.PersistentFlags().StringVar(&opts.PromQLTarget.KubeSvc.Namespace, "metrics-namespace", opts.PromQLTarget.KubeSvc.Namespace, "The name of the namespace where the the remote PromQL query service resides. Must be specified when --use-dns-for-services is disabled.")

pkg/cvo/metrics.go

Lines changed: 9 additions & 205 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
package cvo
22

33
import (
4-
"bytes"
54
"context"
6-
"crypto/sha256"
7-
"crypto/tls"
8-
"errors"
95
"fmt"
10-
"io"
116
"net"
127
"net/http"
13-
"os"
14-
"path/filepath"
158
"time"
169

1710
"github.com/prometheus/client_golang/prometheus"
@@ -26,9 +19,6 @@ import (
2619
configv1 "github.com/openshift/api/config/v1"
2720
"github.com/openshift/cluster-version-operator/lib/resourcemerge"
2821
"github.com/openshift/cluster-version-operator/pkg/internal"
29-
"github.com/openshift/library-go/pkg/crypto"
30-
31-
"gopkg.in/fsnotify.v1"
3222
)
3323

3424
// RegisterMetrics initializes metrics and registers them with the
@@ -136,28 +126,18 @@ func createHttpServer() *http.Server {
136126
return server
137127
}
138128

139-
func shutdownHttpServer(parentCtx context.Context, svr *http.Server) {
140-
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second)
141-
defer cancel()
142-
klog.Info("Shutting down metrics server so it can be recreated with updated TLS configuration.")
143-
if err := svr.Shutdown(ctx); err != nil {
144-
klog.Errorf("Failed to gracefully shut down metrics server during restart: %v", err)
145-
}
146-
}
147-
148-
func startListening(svr *http.Server, tlsConfig *tls.Config, lAddr string, resultChannel chan asyncResult) {
149-
tcpListener, err := net.Listen("tcp", lAddr)
129+
func startListening(svr *http.Server, lAddr string, resultChannel chan asyncResult) {
130+
listener, err := net.Listen("tcp", lAddr)
150131
if err != nil {
151132
resultChannel <- asyncResult{
152-
name: "HTTPS server",
133+
name: "HTTP server",
153134
error: fmt.Errorf("failed to listen to the network address %s reserved for cluster-version-operator metrics: %w", lAddr, err),
154135
}
155136
return
156137
}
157-
tlsListener := tls.NewListener(tcpListener, tlsConfig)
158-
klog.Infof("Metrics port listening for HTTPS on %v", lAddr)
159-
err = svr.Serve(tlsListener)
160-
resultChannel <- asyncResult{name: "HTTPS server", error: err}
138+
klog.Infof("Metrics port listening for HTTP on %v", lAddr)
139+
err = svr.Serve(listener)
140+
resultChannel <- asyncResult{name: "HTTP server", error: err}
161141
}
162142

163143
func handleServerResult(result asyncResult, lastLoopError error) error {
@@ -174,61 +154,19 @@ func handleServerResult(result asyncResult, lastLoopError error) error {
174154
}
175155

176156
// RunMetrics launches a server bound to listenAddress serving
177-
// Prometheus metrics at /metrics over HTTPS. Continues serving
157+
// Prometheus metrics at /metrics over HTTP. Continues serving
178158
// until runContext.Done() and then attempts a clean shutdown
179159
// limited by shutdownContext.Done(). Assumes runContext.Done()
180160
// occurs before or simultaneously with shutdownContext.Done().
181-
// Also detects changes to metrics certificate files upon which
182-
// the metrics HTTP server is shutdown and recreated with a new
183-
// TLS configuration.
184-
func RunMetrics(runContext context.Context, shutdownContext context.Context, listenAddress, certFile, keyFile string) error {
185-
var tlsConfig *tls.Config
186-
if listenAddress != "" {
187-
var err error
188-
tlsConfig, err = makeTLSConfig(certFile, keyFile)
189-
if err != nil {
190-
return fmt.Errorf("Failed to create TLS config: %w", err)
191-
}
192-
} else {
193-
return errors.New("TLS configuration is required to serve metrics")
194-
}
161+
func RunMetrics(runContext context.Context, shutdownContext context.Context, listenAddress string) error {
195162
server := createHttpServer()
196163

197164
resultChannel := make(chan asyncResult, 1)
198165
resultChannelCount := 1
199166

200-
go startListening(server, tlsConfig, listenAddress, resultChannel)
201-
202-
certDir := filepath.Dir(certFile)
203-
keyDir := filepath.Dir(keyFile)
204-
205-
origCertChecksum, err := checksumFile(certFile)
206-
if err != nil {
207-
return fmt.Errorf("Failed to initialize certificate file checksum: %w", err)
208-
}
209-
origKeyChecksum, err := checksumFile(keyFile)
210-
if err != nil {
211-
return fmt.Errorf("Failed to initialize key file checksum: %w", err)
212-
}
213-
214-
// Set up and start the file watcher.
215-
watcher, err := fsnotify.NewWatcher()
216-
if watcher == nil || err != nil {
217-
return fmt.Errorf("Failed to create file watcher for certificate and key rotation: %w", err)
218-
} else {
219-
defer watcher.Close()
220-
if err := watcher.Add(certDir); err != nil {
221-
return fmt.Errorf("Failed to add %v to watcher: %w", certDir, err)
222-
}
223-
if certDir != keyDir {
224-
if err := watcher.Add(keyDir); err != nil {
225-
return fmt.Errorf("Failed to add %v to watcher: %w", keyDir, err)
226-
}
227-
}
228-
}
167+
go startListening(server, listenAddress, resultChannel)
229168

230169
shutdown := false
231-
restartServer := false
232170
var loopError error
233171
for resultChannelCount > 0 {
234172
if shutdown {
@@ -244,53 +182,8 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
244182
select {
245183
case <-runContext.Done(): // clean shutdown
246184
case result := <-resultChannel: // crashed before a shutdown was requested or metrics server recreated
247-
if restartServer {
248-
klog.Info("Creating metrics server with updated TLS configuration.")
249-
server = createHttpServer()
250-
go startListening(server, tlsConfig, listenAddress, resultChannel)
251-
restartServer = false
252-
continue
253-
}
254185
resultChannelCount--
255186
loopError = handleServerResult(result, loopError)
256-
case event := <-watcher.Events:
257-
if event.Op != fsnotify.Chmod && event.Op != fsnotify.Remove {
258-
if changed, err := certsChanged(origCertChecksum, origKeyChecksum, certFile, keyFile); changed {
259-
260-
// Update file checksums with latest files.
261-
//
262-
if origCertChecksum, err = checksumFile(certFile); err != nil {
263-
klog.Errorf("Failed to update certificate file checksum: %v", err)
264-
loopError = err
265-
break
266-
}
267-
if origKeyChecksum, err = checksumFile(keyFile); err != nil {
268-
klog.Errorf("Failed to update key file checksum: %v", err)
269-
loopError = err
270-
break
271-
}
272-
273-
tlsConfig, err = makeTLSConfig(certFile, keyFile)
274-
if err == nil {
275-
restartServer = true
276-
shutdownHttpServer(shutdownContext, server)
277-
continue
278-
} else {
279-
klog.Errorf("Failed to create TLS configuration with updated configuration: %v", err)
280-
loopError = err
281-
}
282-
} else if err != nil {
283-
klog.Errorf("%v", err)
284-
loopError = err
285-
} else {
286-
continue
287-
}
288-
} else {
289-
continue
290-
}
291-
case err = <-watcher.Errors:
292-
klog.Errorf("Error from metrics server certificate file watcher: %v", err)
293-
loopError = err
294187
}
295188
shutdown = true
296189
shutdownError := server.Shutdown(shutdownContext)
@@ -637,92 +530,3 @@ func mostRecentTimestamp(cv *configv1.ClusterVersion) int64 {
637530
}
638531
return latest.Unix()
639532
}
640-
641-
// Determine if the certificates have changed and need to be updated.
642-
// If no errors occur, returns true if both files have changed and
643-
// neither is an empty file. Otherwise returns false and any error.
644-
func certsChanged(origCertChecksum []byte, origKeyChecksum []byte, certFile, keyFile string) (bool, error) {
645-
// Check if both files exist.
646-
certNotEmpty, err := fileExistsAndNotEmpty(certFile)
647-
if err != nil {
648-
return false, fmt.Errorf("Error checking if changed TLS cert file empty/exists: %w", err)
649-
}
650-
keyNotEmpty, err := fileExistsAndNotEmpty(keyFile)
651-
if err != nil {
652-
return false, fmt.Errorf("Error checking if changed TLS key file empty/exists: %w", err)
653-
}
654-
if !certNotEmpty || !keyNotEmpty {
655-
// One of the files is missing despite some file event.
656-
return false, fmt.Errorf("Certificate or key is missing or empty, certificates will not be rotated.")
657-
}
658-
659-
currentCertChecksum, err := checksumFile(certFile)
660-
if err != nil {
661-
return false, fmt.Errorf("Error checking certificate file checksum: %w", err)
662-
}
663-
664-
currentKeyChecksum, err := checksumFile(keyFile)
665-
if err != nil {
666-
return false, fmt.Errorf("Error checking key file checksum: %w", err)
667-
}
668-
669-
// Check if the non-empty certificate/key files have actually changed.
670-
if !bytes.Equal(origCertChecksum, currentCertChecksum) && !bytes.Equal(origKeyChecksum, currentKeyChecksum) {
671-
klog.V(2).Info("Certificate and key changed. Will recreate metrics server with updated TLS configuration.")
672-
return true, nil
673-
}
674-
675-
return false, nil
676-
}
677-
678-
func makeTLSConfig(servingCertFile, servingKeyFile string) (*tls.Config, error) {
679-
// Load the initial certificate contents.
680-
certBytes, err := os.ReadFile(servingCertFile)
681-
if err != nil {
682-
return nil, err
683-
}
684-
keyBytes, err := os.ReadFile(servingKeyFile)
685-
if err != nil {
686-
return nil, err
687-
}
688-
certificate, err := tls.X509KeyPair(certBytes, keyBytes)
689-
if err != nil {
690-
return nil, err
691-
}
692-
693-
return crypto.SecureTLSConfig(&tls.Config{
694-
GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
695-
return &certificate, nil
696-
},
697-
}), nil
698-
}
699-
700-
// Compute the sha256 checksum for file 'fName' returning any error.
701-
func checksumFile(fName string) ([]byte, error) {
702-
file, err := os.Open(fName)
703-
if err != nil {
704-
return nil, fmt.Errorf("Failed to open file %v for checksum: %w", fName, err)
705-
}
706-
defer file.Close()
707-
708-
hash := sha256.New()
709-
710-
if _, err = io.Copy(hash, file); err != nil {
711-
return nil, fmt.Errorf("Failed to compute checksum for file %v: %w", fName, err)
712-
}
713-
714-
return hash.Sum(nil), nil
715-
}
716-
717-
// Check if a file exists and has file.Size() not equal to 0.
718-
// Returns any error returned by os.Stat other than os.ErrNotExist.
719-
func fileExistsAndNotEmpty(fName string) (bool, error) {
720-
if fi, err := os.Stat(fName); err == nil {
721-
return (fi.Size() != 0), nil
722-
} else if errors.Is(err, os.ErrNotExist) {
723-
return false, nil
724-
} else {
725-
// Some other error, file may not exist.
726-
return false, err
727-
}
728-
}

pkg/start/start.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ const (
5858

5959
// Options are the valid inputs to starting the CVO.
6060
type Options struct {
61-
ReleaseImage string
62-
ServingCertFile string
63-
ServingKeyFile string
61+
ReleaseImage string
6462

6563
Kubeconfig string
6664
NodeName string
@@ -139,12 +137,6 @@ func (o *Options) ValidateAndComplete() error {
139137
if o.ReleaseImage == "" {
140138
return fmt.Errorf("missing --release-image flag, it is required")
141139
}
142-
if o.ListenAddr != "" && o.ServingCertFile == "" {
143-
return fmt.Errorf("--listen was not set empty, so --serving-cert-file must be set")
144-
}
145-
if o.ListenAddr != "" && o.ServingKeyFile == "" {
146-
return fmt.Errorf("--listen was not set empty, so --serving-key-file must be set")
147-
}
148140
if o.PrometheusURLString == "" {
149141
return fmt.Errorf("missing --metrics-url flag, it is required")
150142
}
@@ -357,7 +349,7 @@ func (o *Options) run(ctx context.Context, controllerCtx *Context, lock resource
357349
resultChannelCount++
358350
go func() {
359351
defer utilruntime.HandleCrash()
360-
err := cvo.RunMetrics(postMainContext, shutdownContext, o.ListenAddr, o.ServingCertFile, o.ServingKeyFile)
352+
err := cvo.RunMetrics(postMainContext, shutdownContext, o.ListenAddr)
361353
resultChannel <- asyncResult{name: "metrics server", error: err}
362354
}()
363355
}

0 commit comments

Comments
 (0)