Skip to content

Commit

Permalink
Add file watcher for TLS cert and key
Browse files Browse the repository at this point in the history
Also adds error for missing either tls-key or tls-cert arguments.

Signed-off-by: Tayler Geiger <tayler@redhat.com>
  • Loading branch information
trgeiger committed May 8, 2024
1 parent d34081c commit 5391e3e
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 14 deletions.
Binary file added cmd/manager/__debug_bin850815359
Binary file not shown.
34 changes: 21 additions & 13 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ func main() {
flag.StringVar(&cacheDir, "cache-dir", "/var/cache/", "The directory in the filesystem that catalogd will use for file based caching")
flag.BoolVar(&catalogdVersion, "version", false, "print the catalogd version and exit")
flag.DurationVar(&gcInterval, "gc-interval", 12*time.Hour, "interval in which garbage collection should be run against the catalog content cache")
flag.StringVar(&certFile, "tls-cert", "", "The certificate file used for serving catalog contents over HTTPS")
flag.StringVar(&keyFile, "tls-key", "", "The key file used for serving catalog contents over HTTPS")
flag.StringVar(&certFile, "tls-cert", "", "The certificate file used for serving catalog contents over HTTPS. Requires tls-key.")
flag.StringVar(&keyFile, "tls-key", "", "The key file used for serving catalog contents over HTTPS. Requires tls-cert.")
opts := zap.Options{
Development: true,
}
Expand Down Expand Up @@ -150,29 +150,37 @@ func main() {
os.Exit(1)
}

if certFile != "" && keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
switch {
case certFile == "" && keyFile == "":
listener, err = net.Listen("tcp", catalogServerAddr)
if err != nil {
setupLog.Error(err, "unable to load certificate key pair")
setupLog.Error(err, "unable to create HTTP server listener")
os.Exit(1)
}
externalAddr = "http://" + externalAddr
case certFile != "" && keyFile != "":
tlsFileWatcher := server.TLSCertificateWatcher{
Logger: ctrl.Log.WithName("tls-file-watcher"),
CertFile: certFile,
KeyFile: keyFile,
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS13,
GetCertificate: tlsFileWatcher.GetCertificate,
MinVersion: tls.VersionTLS13,
}
listener, err = tls.Listen("tcp", catalogServerAddr, config)
if err != nil {
setupLog.Error(err, "unable to create HTTPS server listener")
os.Exit(1)
}
externalAddr = "https://" + externalAddr
} else {
listener, err = net.Listen("tcp", catalogServerAddr)
if err != nil {
setupLog.Error(err, "unable to create HTTP server listener")
if err := mgr.Add(&tlsFileWatcher); err != nil {
setupLog.Error(err, "unable to start TLS file watcher")
os.Exit(1)
}
externalAddr = "http://" + externalAddr
default:
externalAddr = "https://" + externalAddr
setupLog.Error(nil, "unable to configure TLS certificates, both tls-cert and tls-key arguments are required")
os.Exit(1)
}

baseStorageURL, err := url.Parse(fmt.Sprintf("%s/catalogs/", externalAddr))
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ toolchain go1.21.8
require (
github.com/blang/semver/v4 v4.0.0
github.com/containerd/containerd v1.7.11
github.com/fsnotify/fsnotify v1.6.0
github.com/go-logr/logr v1.4.1
github.com/google/go-cmp v0.6.0
github.com/google/go-containerregistry v0.15.2
Expand Down Expand Up @@ -75,7 +76,6 @@ require (
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-git/go-git/v5 v5.11.0 // indirect
Expand Down
97 changes: 97 additions & 0 deletions internal/third_party/server/tls_cert_watcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// this is copied from https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/77b08a845e451b695cfa25b79ebe277d85064345/pkg/manager/server.go
// we will remove this once we update to a version of controller-runitme that has this included
// https://github.com/kubernetes-sigs/controller-runtime/pull/2473

package server

import (
"context"
"crypto/tls"
"path"
"sync"

"github.com/fsnotify/fsnotify"
"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/manager"
)

var _ manager.Runnable = (*TLSCertificateWatcher)(nil)

type TLSCertificateWatcher struct {
sync.Mutex
certificate *tls.Certificate
Logger logr.Logger
CertFile string
KeyFile string
}

func (t *TLSCertificateWatcher) Start(ctx context.Context) error {
// Run once on startup
err := t.loadCertificate()
t.Logger.Info("tls-key and tls-cert initialized", "tls-cert", t.CertFile, "tls-key", t.KeyFile)
if err != nil {
return err
}

watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
defer watcher.Close()

if path.Dir(t.CertFile) == path.Dir(t.KeyFile) {
watcher.Add(path.Dir(t.CertFile))
} else {
watcher.Add(path.Dir(t.CertFile))
watcher.Add(path.Dir(t.KeyFile))
}

for {
select {
case <-ctx.Done():
return ctx.Err()
case event, _ := <-watcher.Events:
if event.Has(fsnotify.Write) {
t.loadCertificate()
t.Logger.Info("write detected, reloaded tls-cert and tls-key", "tls-cert", t.CertFile, "tls-key", t.KeyFile)
}
case err, _ := <-watcher.Errors:
t.Logger.Error(err, "issue watching TLS certificate files")
}
}
}

func (t *TLSCertificateWatcher) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
t.Lock()
defer t.Unlock()
return t.certificate, nil
}

func (t *TLSCertificateWatcher) loadCertificate() error {
t.Lock()
defer t.Unlock()

cert, err := tls.LoadX509KeyPair(t.CertFile, t.KeyFile)
if err != nil {
return err
}

t.certificate = &cert
return nil
}

0 comments on commit 5391e3e

Please sign in to comment.