From da1083e1c2b60de2e64bbb5ab3ede1b633e08f69 Mon Sep 17 00:00:00 2001 From: Viktor Koptev Date: Thu, 31 Oct 2019 23:35:36 +0300 Subject: [PATCH 1/4] add tls support --- elock.go | 20 ++++++++-------- etcd/etcd.go | 63 +++++++++++++++++++++++++++++++++++++++++++++------ main/elock.go | 13 +++++++---- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/elock.go b/elock.go index a03ce7a..a9c5736 100644 --- a/elock.go +++ b/elock.go @@ -20,13 +20,13 @@ import ( type Options struct { EtcdEndpoints []string - - Path string - Slots int - TTL time.Duration - Refresh time.Duration - Debug bool - MinLockTime time.Duration + EtcdTls etcd.TlsOpts + Path string + Slots int + TTL time.Duration + Refresh time.Duration + Debug bool + MinLockTime time.Duration } type Value struct { @@ -137,7 +137,7 @@ func DefaultOptions() Options { // New creates XLock instance func New(options Options) (*XLock, error) { - etcdClient, err := etcd.NewClient(options.EtcdEndpoints, options.Debug) + etcdClient, err := etcd.NewClient(options.EtcdEndpoints, options.EtcdTls, options.Debug) if err != nil { return nil, err @@ -515,7 +515,7 @@ func List(options Options, timeout time.Duration) ([]*Record, error) { defer cancel() } - etcdClient, err := etcd.NewClient(options.EtcdEndpoints, options.Debug) + etcdClient, err := etcd.NewClient(options.EtcdEndpoints, options.EtcdTls, options.Debug) if err != nil { return nil, err @@ -561,7 +561,7 @@ func Remove(options Options, timeout time.Duration, keys []string) error { defer cancel() } - etcdClient, err := etcd.NewClient(options.EtcdEndpoints, options.Debug) + etcdClient, err := etcd.NewClient(options.EtcdEndpoints, options.EtcdTls, options.Debug) if err != nil { return err diff --git a/etcd/etcd.go b/etcd/etcd.go index a2efb4e..4ecf29d 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -2,18 +2,27 @@ package etcd import ( "context" + "crypto/tls" + "crypto/x509" "encoding/json" "fmt" "io/ioutil" "log" "net/http" "net/url" + "os" "strconv" "strings" "sync" "time" ) +type TlsOpts struct { + Ca string `json:"ca"` + Cert string `json:"cert"` + Key string `json:"key"` +} + type Request struct { method string url *url.URL @@ -23,6 +32,14 @@ type Request struct { type Option func(r *Request) +func fileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} + func GET() Option { return func(r *Request) { r.method = "GET" @@ -202,11 +219,11 @@ type Client struct { sync.RWMutex endpoints []string endpointIndex int - - debug bool + transport *http.Transport + debug bool } -func NewClient(endpoints []string, debug bool) (*Client, error) { +func NewClient(endpoints []string, tlsOpts TlsOpts, debug bool) (*Client, error) { // check endpoints parse for _, e := range endpoints { _, err := url.Parse(e) @@ -215,10 +232,34 @@ func NewClient(endpoints []string, debug bool) (*Client, error) { } } - return &Client{ + client := &Client{ endpoints: endpoints, debug: debug, - }, nil + } + if fileExists(tlsOpts.Ca) && fileExists(tlsOpts.Cert) && fileExists(tlsOpts.Key) { + // Load client cert + cert, err := tls.LoadX509KeyPair(tlsOpts.Cert, tlsOpts.Key) + if err != nil { + log.Fatal(err) + } + + // Load CA cert + caCert, err := ioutil.ReadFile(tlsOpts.Ca) + if err != nil { + log.Fatal(err) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + // Setup HTTPS client + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + } + tlsConfig.BuildNameToCertificate() + client.transport = &http.Transport{TLSClientConfig: tlsConfig} + } + return client, nil } func (client *Client) Debug(format string, v ...interface{}) { @@ -271,8 +312,16 @@ QueryLoop: client.Debug("%s %s", q.method, q.url.String()) - httpClient := &http.Client{ - Timeout: q.timeout, + var httpClient *http.Client + if client.transport == (&http.Transport{}) { + httpClient = &http.Client{ + Timeout: q.timeout, + } + } else { + httpClient = &http.Client{ + Timeout: q.timeout, + Transport: client.transport, + } } // @TODO: check error? diff --git a/main/elock.go b/main/elock.go index 74d2fb0..5db2f48 100644 --- a/main/elock.go +++ b/main/elock.go @@ -15,16 +15,18 @@ import ( "time" "github.com/lomik/elock" + "github.com/lomik/elock/etcd" ) const APP = "elock" const VERSION = "0.4.0" type Config struct { - EtcdEndpoints []string `json:"etcd-endpoints"` - EtcdRoot string `json:"etcd-root"` - EtcdTTL string `json:"etcd-default-ttl"` - EtcdRefresh string `json:"etcd-default-refresh"` + EtcdEndpoints []string `json:"etcd-endpoints"` + EtcdRoot string `json:"etcd-root"` + EtcdTTL string `json:"etcd-default-ttl"` + EtcdRefresh string `json:"etcd-default-refresh"` + EtcdTls etcd.TlsOpts `json:"etcd-tls"` } func main() { @@ -136,6 +138,7 @@ Usage: %s [options] etcd_key command if *list { records, err := elock.List(elock.Options{ EtcdEndpoints: config.EtcdEndpoints, + EtcdTls: config.EtcdTls, Path: config.EtcdRoot, Debug: *debug, }, *timeout) @@ -168,6 +171,7 @@ Usage: %s [options] etcd_key command if *remove { err := elock.Remove(elock.Options{ EtcdEndpoints: config.EtcdEndpoints, + EtcdTls: config.EtcdTls, Path: config.EtcdRoot, Debug: *debug, }, *timeout, args) @@ -186,6 +190,7 @@ Usage: %s [options] etcd_key command x, err := elock.New(elock.Options{ EtcdEndpoints: config.EtcdEndpoints, + EtcdTls: config.EtcdTls, Path: filepath.Join(config.EtcdRoot, args[0]), Slots: *slots, TTL: lockTLL, From d632467384b8952870368ef7f81a86c195e3b0ba Mon Sep 17 00:00:00 2001 From: Viktor Koptev Date: Fri, 1 Nov 2019 00:29:20 +0300 Subject: [PATCH 2/4] fix transport detect --- etcd/etcd.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/etcd/etcd.go b/etcd/etcd.go index 4ecf29d..4d217f8 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -219,7 +219,7 @@ type Client struct { sync.RWMutex endpoints []string endpointIndex int - transport *http.Transport + transport http.RoundTripper debug bool } @@ -236,7 +236,7 @@ func NewClient(endpoints []string, tlsOpts TlsOpts, debug bool) (*Client, error) endpoints: endpoints, debug: debug, } - if fileExists(tlsOpts.Ca) && fileExists(tlsOpts.Cert) && fileExists(tlsOpts.Key) { + if tlsOpts.Ca != "" && tlsOpts.Cert != "" && tlsOpts.Key != "" { // Load client cert cert, err := tls.LoadX509KeyPair(tlsOpts.Cert, tlsOpts.Key) if err != nil { @@ -312,16 +312,9 @@ QueryLoop: client.Debug("%s %s", q.method, q.url.String()) - var httpClient *http.Client - if client.transport == (&http.Transport{}) { - httpClient = &http.Client{ - Timeout: q.timeout, - } - } else { - httpClient = &http.Client{ - Timeout: q.timeout, - Transport: client.transport, - } + httpClient := &http.Client{ + Timeout: q.timeout, + Transport: client.transport, } // @TODO: check error? From 829229b75497da1894147349fcf08e2f5b4b4c04 Mon Sep 17 00:00:00 2001 From: Viktor Koptev Date: Fri, 1 Nov 2019 00:31:31 +0300 Subject: [PATCH 3/4] remove cert file check --- etcd/etcd.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/etcd/etcd.go b/etcd/etcd.go index 4d217f8..07779b6 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -32,14 +32,6 @@ type Request struct { type Option func(r *Request) -func fileExists(filename string) bool { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return false - } - return !info.IsDir() -} - func GET() Option { return func(r *Request) { r.method = "GET" From 4b1bf316b9690fb57a617581b8530a9079ca3664 Mon Sep 17 00:00:00 2001 From: Viktor Koptev Date: Fri, 1 Nov 2019 00:42:50 +0300 Subject: [PATCH 4/4] remove os import --- etcd/etcd.go | 1 - 1 file changed, 1 deletion(-) diff --git a/etcd/etcd.go b/etcd/etcd.go index 07779b6..3c16abe 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -10,7 +10,6 @@ import ( "log" "net/http" "net/url" - "os" "strconv" "strings" "sync"