Skip to content

Commit

Permalink
Add functionality to automatically renew the certs
Browse files Browse the repository at this point in the history
  • Loading branch information
Suraj Narwade authored and Nuckal777 committed Mar 22, 2024
1 parent 09954b6 commit 9623817
Show file tree
Hide file tree
Showing 24 changed files with 4,403 additions and 11 deletions.
1 change: 1 addition & 0 deletions vertical-pod-autoscaler/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module k8s.io/autoscaler/vertical-pod-autoscaler
go 1.21

require (
github.com/fsnotify/fsnotify v1.7.0
github.com/golang/mock v1.6.0
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16
Expand Down
2 changes: 2 additions & 0 deletions vertical-pod-autoscaler/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKf
github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Expand Down
95 changes: 89 additions & 6 deletions vertical-pod-autoscaler/pkg/admission-controller/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,24 @@ package main
import (
"os"

"crypto/tls"
"sync"

"k8s.io/klog/v2"

"github.com/fsnotify/fsnotify"
)

// KeypairReloader structs holds cert path and certs
type KeypairReloader struct {
certMu sync.RWMutex
cert *tls.Certificate
caCert []byte
certPath string
keyPath string
caPath string
}

type certsContainer struct {
caCert, serverKey, serverCert []byte
}
Expand All @@ -41,10 +56,78 @@ func readFile(filePath string) []byte {
return res
}

func initCerts(config certsConfig) certsContainer {
res := certsContainer{}
res.caCert = readFile(*config.clientCaFile)
res.serverCert = readFile(*config.tlsCertFile)
res.serverKey = readFile(*config.tlsPrivateKey)
return res
// NewKeypairReloader will load certs on first run and trigger a goroutine for fsnotify watcher
func NewKeypairReloader(config certsConfig) (*KeypairReloader, error) {
result := &KeypairReloader{
certPath: *config.tlsCertFile,
keyPath: *config.tlsPrivateKey,
caPath: *config.clientCaFile,
caCert: readFile(*config.clientCaFile),
}
cert, err := tls.LoadX509KeyPair(*config.tlsCertFile, *config.tlsPrivateKey)
if err != nil {
return nil, err
}
result.cert = &cert

// creates a new file watcher
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}

defer func() {
if err != nil {
watcher.Close()
}
}()

if err := watcher.Add("/etc/tls-certs"); err != nil {
return nil, err
}

go func() {
for {
select {
// watch for events
case event := <-watcher.Events:
// fsnotify.create events will tell us if there are new certs
if event.Op&fsnotify.Create == fsnotify.Create {
klog.Info("Reloading certs")
if err := result.reload(); err != nil {
klog.Infof("Could not load new certs: %v", err)
}
}

// watch for errors
case err := <-watcher.Errors:
klog.Infof("error", err)
}
}
}()

return result, nil
}

// reload loads updated cert and key whenever they are updated
func (kpr *KeypairReloader) reload() error {
newCert, err := tls.LoadX509KeyPair(kpr.certPath, kpr.keyPath)
caCert := readFile(kpr.caPath)
if err != nil {
return err
}
kpr.certMu.Lock()
defer kpr.certMu.Unlock()
kpr.cert = &newCert
kpr.caCert = caCert
return nil
}

// GetCertificateFunc will return function which will be used as tls.Config.GetCertificate
func (kpr *KeypairReloader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
kpr.certMu.RLock()
defer kpr.certMu.RUnlock()
return kpr.cert, nil
}
}
14 changes: 14 additions & 0 deletions vertical-pod-autoscaler/pkg/admission-controller/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
admissionregistration "k8s.io/api/admissionregistration/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
)

Expand Down Expand Up @@ -73,6 +74,19 @@ func configTLS(serverCert, serverKey []byte, minTlsVersion, ciphers string) *tls
}
}

// get a clientset with in-cluster config.
func getClient() *kubernetes.Clientset {
config, err := rest.InClusterConfig()
if err != nil {
klog.Fatal(err)
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
klog.Fatal(err)
}
return clientset
}

// register this webhook admission controller with the kube-apiserver
// by creating MutatingWebhookConfiguration.
func selfRegistration(clientset *kubernetes.Clientset, caCert []byte, namespace, serviceName, url string, registerByURL bool, timeoutSeconds int32) {
Expand Down
18 changes: 13 additions & 5 deletions vertical-pod-autoscaler/pkg/admission-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"crypto/tls"
"flag"
"fmt"
"net/http"
Expand Down Expand Up @@ -86,8 +87,12 @@ func main() {
metrics.Initialize(*address, healthCheck)
metrics_admission.Register()

certs := initCerts(*certsConfiguration)
config := common.CreateKubeConfigOrDie(*kubeconfig, float32(*kubeApiQps), int(*kubeApiBurst))
// load certs
kpr, err := NewKeypairReloader(*certsConfiguration)
if err != nil {
klog.Fatal(err)
}

vpaClient := vpa_clientset.NewForConfigOrDie(config)
vpaLister := vpa_api_util.NewVpasLister(vpaClient, make(chan struct{}), *vpaObjectNamespace)
Expand All @@ -98,7 +103,7 @@ func main() {
podPreprocessor := pod.NewDefaultPreProcessor()
vpaPreprocessor := vpa.NewDefaultPreProcessor()
var limitRangeCalculator limitrange.LimitRangeCalculator
limitRangeCalculator, err := limitrange.NewLimitsRangeCalculator(factory)
limitRangeCalculator, err = limitrange.NewLimitsRangeCalculator(factory)
if err != nil {
klog.Errorf("Failed to create limitRangeCalculator, falling back to not checking limits. Error message: %s", err)
limitRangeCalculator = limitrange.NewNoopLimitsCalculator()
Expand Down Expand Up @@ -132,13 +137,16 @@ func main() {
healthCheck.UpdateLastActivity()
})
server := &http.Server{
Addr: fmt.Sprintf(":%d", *port),
TLSConfig: configTLS(certs.serverCert, certs.serverKey, *minTlsVersion, *ciphers),
Addr: fmt.Sprintf(":%d", *port),
}
// this will check if there are new certs before every tls handshake
t := &tls.Config{GetCertificate: kpr.GetCertificateFunc()}
server.TLSConfig = t

url := fmt.Sprintf("%v:%v", *webhookAddress, *webhookPort)
go func() {
if *registerWebhook {
selfRegistration(kubeClient, certs.caCert, namespace, *serviceName, url, *registerByURL, int32(*webhookTimeout))
selfRegistration(kubeClient, kpr.caCert, namespace, *serviceName, url, *registerByURL, int32(*webhookTimeout))
}
// Start status updates after the webhook is initialized.
statusUpdater.Run(stopCh)
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9623817

Please sign in to comment.