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..3c16abe 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -2,6 +2,8 @@ package etcd import ( "context" + "crypto/tls" + "crypto/x509" "encoding/json" "fmt" "io/ioutil" @@ -14,6 +16,12 @@ import ( "time" ) +type TlsOpts struct { + Ca string `json:"ca"` + Cert string `json:"cert"` + Key string `json:"key"` +} + type Request struct { method string url *url.URL @@ -202,11 +210,11 @@ type Client struct { sync.RWMutex endpoints []string endpointIndex int - - debug bool + transport http.RoundTripper + 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 +223,34 @@ func NewClient(endpoints []string, debug bool) (*Client, error) { } } - return &Client{ + client := &Client{ endpoints: endpoints, debug: debug, - }, nil + } + if tlsOpts.Ca != "" && tlsOpts.Cert != "" && 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{}) { @@ -272,7 +304,8 @@ QueryLoop: client.Debug("%s %s", q.method, q.url.String()) httpClient := &http.Client{ - Timeout: q.timeout, + 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,