Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

controller refresh certificate #3489

Merged
merged 9 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/extensions/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ func main() {
}
// https server and the items that share the Mux for routing
httpsServer := https.NewServer(ctlConf.CertFile, ctlConf.KeyFile)
cancelTLS, err := httpsServer.WatchForCertificateChanges()
if err != nil {
logger.WithError(err).Fatal("Got an error while watching certificate changes")
}
defer cancelTLS()
wh := webhooks.NewWebHook(httpsServer.Mux)
api := apiserver.NewAPIServer(httpsServer.Mux)

Expand Down
83 changes: 72 additions & 11 deletions pkg/util/https/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,111 @@ package https

import (
"context"
cryptotls "crypto/tls"
"net/http"
"sync"
"time"

"agones.dev/agones/pkg/util/fswatch"
"agones.dev/agones/pkg/util/runtime"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

const (
tlsDir = "/certs/"
)

// tls is a http server interface to enable easier testing
type tls interface {
Close() error
ListenAndServeTLS(certFile, keyFile string) error
}

// CertServer holds the Server certificate
type CertServer struct {
Certs *cryptotls.Certificate
CertMu sync.Mutex
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these fields doesn't have to exported (beginning with upper case) since they are only used in this package, right? Better to change them to unexported for stricter restriction. Similar for the CertServer in Server.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! Thanks for pointing. It should be unexported type.

}

// Server is a HTTPs server that conforms to the runner interface
// we use in /cmd/controller, and has a public Mux that can be updated
// has a default 404 handler, to make discovery of k8s services a bit easier.
type Server struct {
logger *logrus.Entry
Mux *http.ServeMux
tls tls
certFile string
keyFile string
CertServer CertServer
logger *logrus.Entry
Mux *http.ServeMux
tls tls
certFile string
keyFile string
}

// NewServer returns a Server instance.
func NewServer(certFile, keyFile string) *Server {
mux := http.NewServeMux()
tls := &http.Server{
Addr: ":8081",
Handler: mux,
}

wh := &Server{
Mux: mux,
tls: tls,
certFile: certFile,
keyFile: keyFile,
}
wh.Mux.HandleFunc("/", wh.defaultHandler)
wh.logger = runtime.NewLoggerWithType(wh)
wh.setupServer()
wh.Mux.HandleFunc("/", wh.defaultHandler)

return wh
}

func (s *Server) setupServer() {
s.tls = &http.Server{
Addr: ":8081",
Handler: s.Mux,
TLSConfig: &cryptotls.Config{
GetCertificate: s.getCertificate,
},
}

tlsCert, err := cryptotls.LoadX509KeyPair(tlsDir+"server.crt", tlsDir+"server.key")
if err != nil {
s.logger.WithError(err).Error("could not load Initial TLS certs; keeping old one")
return
}

s.CertServer.CertMu.Lock()
defer s.CertServer.CertMu.Unlock()
s.CertServer.Certs = &tlsCert
}

// getCertificate returns the current TLS certificate
func (s *Server) getCertificate(hello *cryptotls.ClientHelloInfo) (*cryptotls.Certificate, error) {
s.CertServer.CertMu.Lock()
defer s.CertServer.CertMu.Unlock()
return s.CertServer.Certs, nil
}

// WatchForCertificateChanges watches for changes in the certificate files
func (s *Server) WatchForCertificateChanges() (func(), error) {

cancelTLS, err := fswatch.Watch(s.logger, tlsDir, time.Second, func() {
// Load the new TLS certificate
s.logger.Info("TLS certs changed, reloading")
tlsCert, err := cryptotls.LoadX509KeyPair(tlsDir+"server.crt", tlsDir+"server.key")
gongmax marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
s.logger.WithError(err).Error("could not load TLS certs; keeping old one")
return
}
s.CertServer.CertMu.Lock()
defer s.CertServer.CertMu.Unlock()
s.CertServer.Certs = &tlsCert
s.logger.Info("TLS certs updated")
})
if err != nil {
s.logger.WithError(err).Fatal("could not create watcher for TLS certs")
return nil, err
}
return cancelTLS, nil
}

// Run runs the webhook server, starting a https listener.
// Will close the http server on stop channel close.
func (s *Server) Run(ctx context.Context, _ int) error {
Expand Down