diff --git a/checker/etcd_leader_checker.go b/checker/etcd_leader_checker.go index e0faf579..189bdcfd 100644 --- a/checker/etcd_leader_checker.go +++ b/checker/etcd_leader_checker.go @@ -2,13 +2,26 @@ package checker import ( "context" + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" "log" + "net" + "net/http" + "os" "time" "github.com/coreos/etcd/client" "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 @@ -18,14 +31,77 @@ type EtcdLeaderChecker struct { //naming this c_conf to avoid conflict with conf in etcd_leader_checker.go var e_conf vipconfig.Config +func getConfigParameter(conf string, env string) string { + if conf == "none" || conf == "" { + return os.Getenv(env) + } + + 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) + } + + certFile := getConfigParameter(conf.Etcd_cert_file, envEtcdCertFile) + + 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} + } + + var tlsClientConfig *tls.Config + + if certificates != nil || caCertPool != nil { + tlsClientConfig = &tls.Config{ + RootCAs: caCertPool, + Certificates: certificates, + } + } + + 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(e_conf) + 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, diff --git a/main.go b/main.go index 50ca0c50..57fe6f44 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") @@ -63,13 +66,14 @@ 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 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/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. 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"