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

Target allocator TLS Implementation #239

Merged
merged 11 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ IMG ?= ${IMG_PREFIX}/${IMG_REPO}:${VERSION}
ARCH ?= $(shell go env GOARCH)

TARGET_ALLOCATOR_IMG_REPO ?= target-allocator
TARGET_ALLOCATOR_IMG ?= ${IMG_PREFIX}/${TARGET_ALLOCATOR_IMG_REPO}:${TARGET_ALLOCATOR_VERSION}
TARGET_ALLOCATOR_IMG ?=${IMG_PREFIX}/${TARGET_ALLOCATOR_IMG_REPO}:${TARGET_ALLOCATOR_VERSION}

# Options for 'bundle-build'
ifneq ($(origin CHANNELS), undefined)
Expand Down Expand Up @@ -103,7 +103,7 @@ manager: generate fmt vet
# Build target allocator binary
.PHONY: targetallocator
targetallocator:
cd cmd/amazon-cloudwatch-agent-target-allocator && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(ARCH) go build -installsuffix cgo -o bin/targetallocator_${ARCH} -ldflags "${LDFLAGS}" .
cd cmd/amazon-cloudwatch-agent-target-allocator && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(ARCH) go build -installsuffix cgo -o bin/targetallocator_${ARCH} -ldflags "${LDFLAGS}" .

# Run against the configured Kubernetes cluster in ~/.kube/config
.PHONY: run
Expand Down
2 changes: 2 additions & 0 deletions cmd/amazon-cloudwatch-agent-target-allocator/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ COPY --from=certificates /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-ce
# Copy binary built on the host
COPY bin/targetallocator_${TARGETARCH} ./main

EXPOSE 8443

ENTRYPOINT ["./main"]
108 changes: 89 additions & 19 deletions cmd/amazon-cloudwatch-agent-target-allocator/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package config

import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/fs"
Expand All @@ -16,38 +18,59 @@ import (
_ "github.com/prometheus/prometheus/discovery/install"
"github.com/spf13/pflag"
"gopkg.in/yaml.v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"

tamanifest "github.com/aws/amazon-cloudwatch-agent-operator/internal/manifests/targetallocator"
)

const DefaultResyncTime = 5 * time.Minute
const DefaultConfigFilePath string = "/conf/targetallocator.yaml"
const DefaultCRScrapeInterval model.Duration = model.Duration(time.Second * 30)
const DefaultAllocationStrategy string = "consistent-hashing"
const (
DefaultResyncTime = 5 * time.Minute
DefaultConfigFilePath string = "/conf/targetallocator.yaml"
DefaultCRScrapeInterval model.Duration = model.Duration(time.Second * 30)
DefaultAllocationStrategy = "consistent-hashing"
DefaultFilterStrategy = "relabel-config"
DefaultListenAddr = ":8443"
DefaultCertMountPath = tamanifest.TACertMountPath
DefaultTLSKeyPath = DefaultCertMountPath + "/server.key"
DefaultTLSCertPath = DefaultCertMountPath + "/server.crt"
DefaultCABundlePath = ""
)

type Config struct {
ListenAddr string `yaml:"listen_addr,omitempty"`
KubeConfigFilePath string `yaml:"kube_config_file_path,omitempty"`
ClusterConfig *rest.Config `yaml:"-"`
RootLogger logr.Logger `yaml:"-"`
ReloadConfig bool `yaml:"-"`
LabelSelector map[string]string `yaml:"label_selector,omitempty"`
PromConfig *promconfig.Config `yaml:"config"`
AllocationStrategy *string `yaml:"allocation_strategy,omitempty"`
FilterStrategy *string `yaml:"filter_strategy,omitempty"`
PrometheusCR PrometheusCRConfig `yaml:"prometheus_cr,omitempty"`
PodMonitorSelector map[string]string `yaml:"pod_monitor_selector,omitempty"`
ServiceMonitorSelector map[string]string `yaml:"service_monitor_selector,omitempty"`
ListenAddr string `yaml:"listen_addr,omitempty"`
KubeConfigFilePath string `yaml:"kube_config_file_path,omitempty"`
ClusterConfig *rest.Config `yaml:"-"`
RootLogger logr.Logger `yaml:"-"`
ReloadConfig bool `yaml:"-"`
LabelSelector map[string]string `yaml:"label_selector,omitempty"`
PromConfig *promconfig.Config `yaml:"config"`
AllocationStrategy *string `yaml:"allocation_strategy,omitempty"`
FilterStrategy *string `yaml:"filter_strategy,omitempty"`
PrometheusCR PrometheusCRConfig `yaml:"prometheus_cr,omitempty"`
PodMonitorSelector map[string]string `yaml:"pod_monitor_selector,omitempty"`
ServiceMonitorSelector map[string]string `yaml:"service_monitor_selector,omitempty"`
CollectorSelector *metav1.LabelSelector `yaml:"collector_selector,omitempty"`
HTTPS HTTPSServerConfig `yaml:"https,omitempty"`
}

type PrometheusCRConfig struct {
Enabled bool `yaml:"enabled,omitempty"`
ScrapeInterval model.Duration `yaml:"scrape_interval,omitempty"`
}

type HTTPSServerConfig struct {
Enabled bool `yaml:"enabled,omitempty"`
ListenAddr string `yaml:"listen_addr,omitempty"`
CAFilePath string `yaml:"ca_file_path,omitempty"`
TLSCertFilePath string `yaml:"tls_cert_file_path,omitempty"`
TLSKeyFilePath string `yaml:"tls_key_file_path,omitempty"`
}

func (c Config) GetAllocationStrategy() string {
if c.AllocationStrategy != nil {
return *c.AllocationStrategy
Expand Down Expand Up @@ -91,17 +114,32 @@ func LoadFromCLI(target *Config, flagSet *pflag.FlagSet) error {
}
target.ClusterConfig = clusterConfig

target.ListenAddr, err = getListenAddr(flagSet)
target.ReloadConfig, err = getConfigReloadEnabled(flagSet)
if err != nil {
return err
}

target.PrometheusCR.Enabled, err = getPrometheusCREnabled(flagSet)
target.HTTPS.Enabled, err = getHttpsEnabled(flagSet)
if err != nil {
return err
}

target.ReloadConfig, err = getConfigReloadEnabled(flagSet)
target.HTTPS.ListenAddr, err = getHttpsListenAddr(flagSet)
if err != nil {
return err
}

target.HTTPS.CAFilePath, err = getHttpsCAFilePath(flagSet)
if err != nil {
return err
}

target.HTTPS.TLSCertFilePath, err = getHttpsTLSCertFilePath(flagSet)
if err != nil {
return err
}

target.HTTPS.TLSKeyFilePath, err = getHttpsTLSKeyFilePath(flagSet)
if err != nil {
return err
}
Expand All @@ -128,6 +166,13 @@ func CreateDefaultConfig() Config {
ScrapeInterval: DefaultCRScrapeInterval,
},
AllocationStrategy: &allocation_strategy,
HTTPS: HTTPSServerConfig{
Enabled: true,
ListenAddr: DefaultListenAddr,
CAFilePath: DefaultCABundlePath,
TLSCertFilePath: DefaultTLSCertPath,
TLSKeyFilePath: DefaultTLSKeyPath,
},
}
}

Expand Down Expand Up @@ -168,3 +213,28 @@ func ValidateConfig(config *Config) error {
}
return nil
}

func (c HTTPSServerConfig) NewTLSConfig() (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(c.TLSCertFilePath, c.TLSKeyFilePath)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.NoClientCert,
MinVersion: tls.VersionTLS12,
}
if c.CAFilePath == "" {
return tlsConfig, nil
}
caCert, err := os.ReadFile(c.CAFilePath)
caCertPool := x509.NewCertPool()
if err != nil {
return nil, err
}
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig.ClientCAs = caCertPool
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert

return tlsConfig, nil
}
14 changes: 14 additions & 0 deletions cmd/amazon-cloudwatch-agent-target-allocator/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ func TestLoad(t *testing.T) {
PrometheusCR: PrometheusCRConfig{
ScrapeInterval: model.Duration(time.Second * 60),
},
HTTPS: HTTPSServerConfig{
Enabled: true,
ListenAddr: DefaultListenAddr,
CAFilePath: "/path/to/ca.pem",
TLSCertFilePath: "/path/to/cert.pem",
TLSKeyFilePath: "/path/to/key.pem",
},
PromConfig: &promconfig.Config{
GlobalConfig: promconfig.GlobalConfig{
ScrapeInterval: model.Duration(60 * time.Second),
Expand Down Expand Up @@ -106,6 +113,13 @@ func TestLoad(t *testing.T) {
PrometheusCR: PrometheusCRConfig{
ScrapeInterval: DefaultCRScrapeInterval,
},
HTTPS: HTTPSServerConfig{
Enabled: true,
ListenAddr: DefaultListenAddr,
CAFilePath: DefaultCABundlePath,
TLSCertFilePath: DefaultTLSCertPath,
TLSKeyFilePath: DefaultTLSKeyPath,
},
PromConfig: &promconfig.Config{
GlobalConfig: promconfig.GlobalConfig{
ScrapeInterval: model.Duration(60 * time.Second),
Expand Down
48 changes: 34 additions & 14 deletions cmd/amazon-cloudwatch-agent-target-allocator/config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ import (

// Flag names.
const (
targetAllocatorName = "target-allocator"
configFilePathFlagName = "config-file"
listenAddrFlagName = "listen-addr"
prometheusCREnabledFlagName = "enable-prometheus-cr-watcher"
kubeConfigPathFlagName = "kubeconfig-path"
reloadConfigFlagName = "reload-config"
targetAllocatorName = "target-allocator"
configFilePathFlagName = "config-file"
listenAddrFlagName = "listen-addr"
prometheusCREnabledFlagName = "enable-prometheus-cr-watcher"
kubeConfigPathFlagName = "kubeconfig-path"
reloadConfigFlagName = "reload-config"
httpsEnabledFlagName = "enable-https-server"
listenAddrHttpsFlagName = "listen-addr-https"
httpsCAFilePathFlagName = "https-ca-file"
httpsTLSCertFilePathFlagName = "https-tls-cert-file"
httpsTLSKeyFilePathFlagName = "https-tls-key-file"
)

// We can't bind this flag to our FlagSet, so we need to handle it separately.
Expand All @@ -28,10 +33,13 @@ var zapCmdLineOpts zap.Options
func getFlagSet(errorHandling pflag.ErrorHandling) *pflag.FlagSet {
flagSet := pflag.NewFlagSet(targetAllocatorName, errorHandling)
flagSet.String(configFilePathFlagName, DefaultConfigFilePath, "The path to the config file.")
flagSet.String(listenAddrFlagName, ":8080", "The address where this service serves.")
flagSet.Bool(prometheusCREnabledFlagName, false, "Enable Prometheus CRs as target sources")
flagSet.String(kubeConfigPathFlagName, filepath.Join(homedir.HomeDir(), ".kube", "config"), "absolute path to the KubeconfigPath file")
flagSet.Bool(reloadConfigFlagName, false, "Enable automatic configuration reloading. This functionality is deprecated and will be removed in a future release.")
flagSet.Bool(httpsEnabledFlagName, true, "Enable HTTPS additional server")
flagSet.String(listenAddrHttpsFlagName, ":8443", "The address where this service serves over HTTPS.")
flagSet.String(httpsCAFilePathFlagName, DefaultCABundlePath, "The path to the HTTPS server TLS CA file.")
flagSet.String(httpsTLSCertFilePathFlagName, DefaultTLSCertPath, "The path to the HTTPS server TLS certificate file.")
flagSet.String(httpsTLSKeyFilePathFlagName, DefaultTLSKeyPath, "The path to the HTTPS server TLS key file.")
zapFlagSet := flag.NewFlagSet("", flag.ErrorHandling(errorHandling))
zapCmdLineOpts.BindFlags(zapFlagSet)
flagSet.AddGoFlagSet(zapFlagSet)
Expand All @@ -46,14 +54,26 @@ func getKubeConfigFilePath(flagSet *pflag.FlagSet) (string, error) {
return flagSet.GetString(kubeConfigPathFlagName)
}

func getListenAddr(flagSet *pflag.FlagSet) (string, error) {
return flagSet.GetString(listenAddrFlagName)
func getConfigReloadEnabled(flagSet *pflag.FlagSet) (bool, error) {
return flagSet.GetBool(reloadConfigFlagName)
}

func getPrometheusCREnabled(flagSet *pflag.FlagSet) (bool, error) {
return flagSet.GetBool(prometheusCREnabledFlagName)
func getHttpsListenAddr(flagSet *pflag.FlagSet) (string, error) {
return flagSet.GetString(listenAddrHttpsFlagName)
}

func getConfigReloadEnabled(flagSet *pflag.FlagSet) (bool, error) {
return flagSet.GetBool(reloadConfigFlagName)
func getHttpsEnabled(flagSet *pflag.FlagSet) (bool, error) {
return flagSet.GetBool(httpsEnabledFlagName)
}

func getHttpsCAFilePath(flagSet *pflag.FlagSet) (string, error) {
return flagSet.GetString(httpsCAFilePathFlagName)
}

func getHttpsTLSCertFilePath(flagSet *pflag.FlagSet) (string, error) {
return flagSet.GetString(httpsTLSCertFilePathFlagName)
}

func getHttpsTLSKeyFilePath(flagSet *pflag.FlagSet) (string, error) {
return flagSet.GetString(httpsTLSKeyFilePathFlagName)
}
26 changes: 12 additions & 14 deletions cmd/amazon-cloudwatch-agent-target-allocator/config/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ func TestGetFlagSet(t *testing.T) {

// Check if each flag exists
assert.NotNil(t, fs.Lookup(configFilePathFlagName), "Flag %s not found", configFilePathFlagName)
assert.NotNil(t, fs.Lookup(listenAddrFlagName), "Flag %s not found", listenAddrFlagName)
assert.NotNil(t, fs.Lookup(prometheusCREnabledFlagName), "Flag %s not found", prometheusCREnabledFlagName)
assert.NotNil(t, fs.Lookup(kubeConfigPathFlagName), "Flag %s not found", kubeConfigPathFlagName)
}

Expand All @@ -41,18 +39,6 @@ func TestFlagGetters(t *testing.T) {
expectedValue: filepath.Join("~", ".kube", "config"),
getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getKubeConfigFilePath(fs) },
},
{
name: "GetListenAddr",
flagArgs: []string{"--" + listenAddrFlagName, ":8081"},
expectedValue: ":8081",
getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getListenAddr(fs) },
},
{
name: "GetPrometheusCREnabled",
flagArgs: []string{"--" + prometheusCREnabledFlagName, "true"},
expectedValue: true,
getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getPrometheusCREnabled(fs) },
},
{
name: "GetConfigReloadEnabled",
flagArgs: []string{"--" + reloadConfigFlagName, "true"},
Expand All @@ -65,6 +51,18 @@ func TestFlagGetters(t *testing.T) {
expectedErr: true,
getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getConfigFilePath(fs) },
},
{
name: "HttpsServer",
flagArgs: []string{"--" + httpsEnabledFlagName, "true"},
expectedValue: true,
getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getHttpsEnabled(fs) },
},
{
name: "HttpsServerKey",
flagArgs: []string{"--" + httpsTLSKeyFilePathFlagName, "/path/to/tls.key"},
expectedValue: "/path/to/tls.key",
getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getHttpsTLSKeyFilePath(fs) },
},
}

for _, tt := range tests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ label_selector:
app.kubernetes.io/managed-by: amazon-cloudwatch-agent-operator
prometheus_cr:
scrape_interval: 60s
https:
enabled: true
ca_file_path: /path/to/ca.pem
tls_cert_file_path: /path/to/cert.pem
tls_key_file_path: /path/to/key.pem
config:
scrape_configs:
- job_name: prometheus
Expand Down
24 changes: 17 additions & 7 deletions cmd/amazon-cloudwatch-agent-target-allocator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ func main() {
errChan = make(chan error)
)
cfg, configFilePath, err := config.Load()

if err != nil {
fmt.Printf("Failed to load config: %v", err)
os.Exit(1)
}
setupLog.Info("init config", "Config-Http", cfg.HTTPS)
ctrl.SetLogger(cfg.RootLogger)

if validationErr := config.ValidateConfig(cfg); validationErr != nil {
Expand All @@ -76,7 +78,15 @@ func main() {
setupLog.Error(err, "Unable to initialize allocation strategy")
os.Exit(1)
}
srv := server.NewServer(log, allocator, cfg.ListenAddr)

httpOptions := []server.Option{}
tlsConfig, confErr := cfg.HTTPS.NewTLSConfig()
if confErr != nil {
setupLog.Error(confErr, "Unable to initialize TLS configuration", "Config", cfg.HTTPS)
os.Exit(1)
}
httpOptions = append(httpOptions, server.WithTLSConfig(tlsConfig, cfg.HTTPS.ListenAddr))
srv := server.NewServer(log, allocator, cfg.ListenAddr, httpOptions...)

discoveryCtx, discoveryCancel := context.WithCancel(ctx)
discoveryManager = discovery.NewManager(discoveryCtx, gokitlog.NewNopLogger())
Expand Down Expand Up @@ -171,14 +181,14 @@ func main() {
})
runGroup.Add(
func() error {
err := srv.Start()
setupLog.Info("Server failed to start")
err := srv.StartHTTPS()
setupLog.Info("HTTPS Server failed to start", "error", err)
return err
},
func(_ error) {
setupLog.Info("Closing server")
if shutdownErr := srv.Shutdown(ctx); shutdownErr != nil {
setupLog.Error(shutdownErr, "Error on server shutdown")
func(intrpError error) {
setupLog.Info("Closing HTTPS server", "intrp", intrpError)
if shutdownErr := srv.ShutdownHTTPS(ctx); shutdownErr != nil {
setupLog.Error(shutdownErr, "Error on HTTPS server shutdown")
}
})
runGroup.Add(
Expand Down
Loading