From 5a679b44e9e622a33acfa71cd1691aa231c8a24d Mon Sep 17 00:00:00 2001 From: gandalfmagic Date: Wed, 13 Nov 2019 12:13:10 +0100 Subject: [PATCH 1/3] Add TLS support for etcd connections --- checker/etcd_leader_checker.go | 48 +++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/checker/etcd_leader_checker.go b/checker/etcd_leader_checker.go index e0faf579..323fb471 100644 --- a/checker/etcd_leader_checker.go +++ b/checker/etcd_leader_checker.go @@ -2,7 +2,14 @@ package checker import ( "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" "log" + "net" + "net/http" + "os" "time" "github.com/coreos/etcd/client" @@ -18,14 +25,53 @@ type EtcdLeaderChecker struct { //naming this c_conf to avoid conflict with conf in etcd_leader_checker.go var e_conf vipconfig.Config +func getTransport() (client.CancelableTransport, error) { + if os.Getenv("ETCD_CLIENT_CERT_AUTH") != "true" { + return client.DefaultTransport, nil + } + + cert, err := ioutil.ReadFile(os.Getenv("ETCD_TRUSTED_CA_FILE")) + if err != nil { + return nil, fmt.Errorf("cannot load CA file: %s", err) + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(cert) + + cer, err := tls.LoadX509KeyPair(os.Getenv("ETCD_CERT_FILE"), os.Getenv("ETCD_KEY_FILE")) + if err != nil { + return nil, fmt.Errorf("cannot load client cert or key file: %s", err) + } + + tlsClientConfig := &tls.Config{ + RootCAs: caCertPool, + Certificates: []tls.Certificate{cer}, + } + + return &http.Transport{ + Proxy: http.ProxyFromEnvironment, + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSClientConfig: tlsClientConfig, + TLSHandshakeTimeout: 10 * time.Second, + }, nil +} + //func NewEtcdLeaderChecker(endpoint, key, nodename string, etcd_user string, etcd_password string) (*EtcdLeaderChecker, error) { func NewEtcdLeaderChecker(con vipconfig.Config) (*EtcdLeaderChecker, error) { e_conf = con e := &EtcdLeaderChecker{key: e_conf.Key, nodename: e_conf.Nodename} + transport, err := getTransport() + if err != nil { + return nil, err + } + cfg := client.Config{ Endpoints: e_conf.Endpoints, - Transport: client.DefaultTransport, + Transport: transport, HeaderTimeoutPerRequest: time.Second, Username: e_conf.Etcd_user, Password: e_conf.Etcd_password, From edc2ebf8de51a11d521e289251094b291c028edc Mon Sep 17 00:00:00 2001 From: gandalfmagic Date: Thu, 14 Nov 2019 15:05:08 +0100 Subject: [PATCH 2/3] Enable TLS configuration from cli flags and from yaml file --- checker/etcd_leader_checker.go | 62 +++++++++++++++++++++++++--------- main.go | 6 +++- vipconfig/config.go | 15 ++++---- vipconfig/vip-manager.yml | 4 +++ 4 files changed, 64 insertions(+), 23 deletions(-) diff --git a/checker/etcd_leader_checker.go b/checker/etcd_leader_checker.go index 323fb471..189bdcfd 100644 --- a/checker/etcd_leader_checker.go +++ b/checker/etcd_leader_checker.go @@ -16,6 +16,12 @@ import ( "github.com/cybertec-postgresql/vip-manager/vipconfig" ) +const ( + envEtcdCaFile = "ETCD_TRUSTED_CA_FILE" + envEtcdCertFile = "ETCD_CERT_FILE" + envEtcdKeyFile = "ETCD_KEY_FILE" +) + type EtcdLeaderChecker struct { key string nodename string @@ -25,27 +31,51 @@ type EtcdLeaderChecker struct { //naming this c_conf to avoid conflict with conf in etcd_leader_checker.go var e_conf vipconfig.Config -func getTransport() (client.CancelableTransport, error) { - if os.Getenv("ETCD_CLIENT_CERT_AUTH") != "true" { - return client.DefaultTransport, nil +func getConfigParameter(conf string, env string) string { + if conf == "none" || conf == "" { + return os.Getenv(env) } - cert, err := ioutil.ReadFile(os.Getenv("ETCD_TRUSTED_CA_FILE")) - if err != nil { - return nil, fmt.Errorf("cannot load CA file: %s", err) + return conf +} + +func getTransport(conf vipconfig.Config) (client.CancelableTransport, error) { + var caCertPool *x509.CertPool + + // create valid CertPool only if the ca certificate file exists + if caCertFile := getConfigParameter(conf.Etcd_ca_file, envEtcdCaFile); caCertFile != "" { + caCert, err := ioutil.ReadFile(caCertFile) + if err != nil { + return nil, fmt.Errorf("cannot load CA file: %s", err) + } + + caCertPool = x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) } - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(cert) + certFile := getConfigParameter(conf.Etcd_cert_file, envEtcdCertFile) - cer, err := tls.LoadX509KeyPair(os.Getenv("ETCD_CERT_FILE"), os.Getenv("ETCD_KEY_FILE")) - if err != nil { - return nil, fmt.Errorf("cannot load client cert or key file: %s", err) + keyFile := getConfigParameter(conf.Etcd_key_file, envEtcdKeyFile) + + var certificates []tls.Certificate + + // create valid []Certificate only if the client cert and key files exists + if certFile != "" && keyFile != "" { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return nil, fmt.Errorf("cannot load client cert or key file: %s", err) + } + + certificates = []tls.Certificate{cert} } - tlsClientConfig := &tls.Config{ - RootCAs: caCertPool, - Certificates: []tls.Certificate{cer}, + var tlsClientConfig *tls.Config + + if certificates != nil || caCertPool != nil { + tlsClientConfig = &tls.Config{ + RootCAs: caCertPool, + Certificates: certificates, + } } return &http.Transport{ @@ -54,7 +84,7 @@ func getTransport() (client.CancelableTransport, error) { Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).Dial, - TLSClientConfig: tlsClientConfig, + TLSClientConfig: tlsClientConfig, TLSHandshakeTimeout: 10 * time.Second, }, nil } @@ -64,7 +94,7 @@ func NewEtcdLeaderChecker(con vipconfig.Config) (*EtcdLeaderChecker, error) { e_conf = con e := &EtcdLeaderChecker{key: e_conf.Key, nodename: e_conf.Nodename} - transport, err := getTransport() + transport, err := getTransport(e_conf) if err != nil { return nil, err } diff --git a/main.go b/main.go index 50ca0c50..7959df5d 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,9 @@ var key = flag.String("key", "none", "key to monitor, e.g. /service/batman/leade var host = flag.String("host", "none", "Value to monitor for") var etcd_user = flag.String("etcd_user", "none", "username that can be used to access the key in etcd") var etcd_password = flag.String("etcd_password", "none", "password for the etcd_user") +var etcd_ca_file = flag.String("etcd_ca_file", "none", "trusted CA certificate for the etcd server") +var etcd_cert_file = flag.String("etcd_cert_file", "none", "etcd client certificate") +var etcd_key_file = flag.String("etcd_key_file", "none", "etcd client private key") var endpointType = flag.String("type", "etcd", "type of endpoint used for key storage. Supported values: etcd, consul") var endpoint = flag.String("endpoint", "http://localhost:2379[,http://host:port,..]", "endpoint") @@ -69,7 +72,8 @@ func main() { //introduce parsed values into conf conf = vipconfig.Config{Ip: *ip, Mask: *mask, Iface: *iface, HostingType: *hostingType, Key: *key, Nodename: *host, Endpoint_type: *endpointType, Endpoints: []string{*endpoint}, - Etcd_user: *etcd_user, Etcd_password: *etcd_password, Interval: *interval} + Etcd_user: *etcd_user, Etcd_password: *etcd_password, Etcd_ca_file: *etcd_ca_file, + Etcd_cert_file: *etcd_cert_file, Etcd_key_file: *etcd_key_file, Interval: *interval} if *configFile != "" { yamlFile, err := ioutil.ReadFile(*configFile) diff --git a/vipconfig/config.go b/vipconfig/config.go index deb9e0ab..601d7928 100644 --- a/vipconfig/config.go +++ b/vipconfig/config.go @@ -12,15 +12,18 @@ type Config struct { Key string `yaml:key` Nodename string `yaml:nodename` //hostname to trigger on. usually the name of the host where this vip-manager runs. - Endpoint_type string `yaml:endpoint_type` - Endpoints []string `yaml:endpoints` - Etcd_user string `yaml:etcd_user` - Etcd_password string `yaml:etcd_password` + Endpoint_type string `yaml:endpoint_type` + Endpoints []string `yaml:endpoints` + Etcd_user string `yaml:etcd_user` + Etcd_password string `yaml:etcd_password` + Etcd_ca_file string `yaml:etcd_ca_file` + Etcd_cert_file string `yaml:etcd_cert_file` + Etcd_key_file string `yaml:etcd_key_file` Consul_token string `yaml:consul_token` - Interval int`yaml:interval` //milliseconds + Interval int `yaml:interval` //milliseconds - Retry_after int `yaml:retry_after` //milliseconds + Retry_after int `yaml:retry_after` //milliseconds Retry_num int `yaml:retry_num` } diff --git a/vipconfig/vip-manager.yml b/vipconfig/vip-manager.yml index fe429590..25a71f85 100644 --- a/vipconfig/vip-manager.yml +++ b/vipconfig/vip-manager.yml @@ -26,6 +26,10 @@ endpoints: etcd_user: "patroni" etcd_password: "Julian's secret password" +etcd_ca_file: "/path/to/etcd/trusted/ca/file" +etcd_cert_file: "/path/to/etcd/client/cert/file" +etcd_key_file: "/path/to/etcd/client/key/file" + # don't worry about parameter with a prefix that doesn't match the endpoint_type. You can write anything there, I won't even look at it. consul_token: "Julian's secret token" From e0ab6d28f2e5058c7952a7b2ec31f37ae51135f4 Mon Sep 17 00:00:00 2001 From: gandalfmagic Date: Sun, 17 Nov 2019 23:55:43 +0100 Subject: [PATCH 3/3] Update version --- main.go | 2 +- package/DEBIAN/changelog | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 7959df5d..57fe6f44 100644 --- a/main.go +++ b/main.go @@ -66,7 +66,7 @@ func main() { flag.Parse() if *versionHint == true { - fmt.Println("version 0.6.1") + fmt.Println("version 0.6.2") return } //introduce parsed values into conf diff --git a/package/DEBIAN/changelog b/package/DEBIAN/changelog index d7035cf4..c6c9debf 100644 --- a/package/DEBIAN/changelog +++ b/package/DEBIAN/changelog @@ -1,3 +1,10 @@ +vip-manager (0.6.2-1) unstable; urgency=low + + * enable TLS configuration from cli flags and from yaml file + * add TLS support for etcd connections + + -- gandalfmagic Sun, 17 Nov 2019 23:52:12 +0100 + vip-manager (0.6-1) unstable; urgency=low * switched to yaml file for config, still supporting all command line options from previous releases.