From db12e5704be3b0f16f9721881445c6e3dca04649 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 08:09:41 -0700 Subject: [PATCH 01/16] transport: no need to set RootCAs in TLSInfo.ServerConfig --- transport/listener.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/transport/listener.go b/transport/listener.go index 122f92d58..c8f15fc9a 100644 --- a/transport/listener.go +++ b/transport/listener.go @@ -59,8 +59,6 @@ func (info TLSInfo) ServerConfig() (*tls.Config, error) { if err != nil { return nil, err } - - cfg.RootCAs = cp cfg.ClientCAs = cp } else { cfg.ClientAuth = tls.NoClientCert From 5470a6d3d6e159fe96c91bc3a5c38bb0e48737ee Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 08:38:26 -0700 Subject: [PATCH 02/16] etcdserver: pass http client into Sender --- etcdserver/etcdhttp/peers.go | 14 +++++++------- etcdserver/etcdhttp/peers_test.go | 6 +++--- main.go | 11 ++++++++++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/etcdserver/etcdhttp/peers.go b/etcdserver/etcdhttp/peers.go index 4af765fe2..e60e780d6 100644 --- a/etcdserver/etcdhttp/peers.go +++ b/etcdserver/etcdhttp/peers.go @@ -85,17 +85,18 @@ func (ps Peers) Endpoints() []string { return endpoints } -func Sender(p Peers) func(msgs []raftpb.Message) { +func Sender(t *http.Transport, p Peers) func(msgs []raftpb.Message) { + c := &http.Client{Transport: t} return func(msgs []raftpb.Message) { for _, m := range msgs { // TODO: reuse go routines // limit the number of outgoing connections for the same receiver - go send(p, m) + go send(c, p, m) } } } -func send(p Peers, m raftpb.Message) { +func send(c *http.Client, p Peers, m raftpb.Message) { // TODO (xiangli): reasonable retry logic for i := 0; i < 3; i++ { url := p.Pick(m.To) @@ -116,16 +117,15 @@ func send(p Peers, m raftpb.Message) { log.Println("etcdhttp: dropping message:", err) return // drop bad message } - if httpPost(url, data) { + if httpPost(c, url, data) { return // success } // TODO: backoff } } -func httpPost(url string, data []byte) bool { - // TODO: set timeouts - resp, err := http.Post(url, "application/protobuf", bytes.NewBuffer(data)) +func httpPost(c *http.Client, url string, data []byte) bool { + resp, err := c.Post(url, "application/protobuf", bytes.NewBuffer(data)) if err != nil { elog.TODO() return false diff --git a/etcdserver/etcdhttp/peers_test.go b/etcdserver/etcdhttp/peers_test.go index f733635b1..e08bc7325 100644 --- a/etcdserver/etcdhttp/peers_test.go +++ b/etcdserver/etcdhttp/peers_test.go @@ -148,7 +148,7 @@ func TestHttpPost(t *testing.T) { } for i, tt := range tests { ts := httptest.NewServer(tt.h) - if g := httpPost(ts.URL, []byte("adsf")); g != tt.w { + if g := httpPost(http.DefaultClient, ts.URL, []byte("adsf")); g != tt.w { t.Errorf("#%d: httpPost()=%t, want %t", i, g, tt.w) } if tr.Method != "POST" { @@ -161,7 +161,7 @@ func TestHttpPost(t *testing.T) { ts.Close() } - if httpPost("garbage url", []byte("data")) { + if httpPost(http.DefaultClient, "garbage url", []byte("data")) { t.Errorf("httpPost with bad URL returned true unexpectedly!") } } @@ -215,7 +215,7 @@ func TestSend(t *testing.T) { ps := Peers{ 42: []string{strings.TrimPrefix(ts.URL, "http://")}, } - send(ps, tt.m) + send(http.DefaultClient, ps, tt.m) if !tt.ok { if tr != nil { diff --git a/main.go b/main.go index c424dc4dc..53350b996 100644 --- a/main.go +++ b/main.go @@ -151,6 +151,15 @@ func startEtcd() { n = raft.RestartNode(id, peers.IDs(), 10, 1, snapshot, st, ents) } + pt := &http.Transport{ + // timeouts copied from http.DefaultTransport + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, + } + s := &etcdserver.EtcdServer{ Store: st, Node: n, @@ -158,7 +167,7 @@ func startEtcd() { *wal.WAL *snap.Snapshotter }{w, snapshotter}, - Send: etcdhttp.Sender(*peers), + Send: etcdhttp.Sender(pt, *peers), Ticker: time.Tick(100 * time.Millisecond), SyncTicker: time.Tick(500 * time.Millisecond), SnapCount: *snapCount, From 0c7351c30923317f0555185d8156563d3fb06ace Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 08:43:20 -0700 Subject: [PATCH 03/16] etcd: manually construct HTTP client for peer communication --- main.go | 10 +++------- transport/listener.go | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/main.go b/main.go index 53350b996..2cd5cf7ee 100644 --- a/main.go +++ b/main.go @@ -151,13 +151,9 @@ func startEtcd() { n = raft.RestartNode(id, peers.IDs(), 10, 1, snapshot, st, ents) } - pt := &http.Transport{ - // timeouts copied from http.DefaultTransport - Dial: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 10 * time.Second, + pt, err := transport.NewTransport() + if err != nil { + log.Fatal(err) } s := &etcdserver.EtcdServer{ diff --git a/transport/listener.go b/transport/listener.go index c8f15fc9a..488bac216 100644 --- a/transport/listener.go +++ b/transport/listener.go @@ -7,6 +7,8 @@ import ( "fmt" "io/ioutil" "net" + "net/http" + "time" ) func NewListener(addr string, info TLSInfo) (net.Listener, error) { @@ -27,6 +29,18 @@ func NewListener(addr string, info TLSInfo) (net.Listener, error) { return l, nil } +func NewTransport() (*http.Transport, error) { + t := &http.Transport{ + // timeouts taken from http.DefaultTransport + Dial: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).Dial, + TLSHandshakeTimeout: 10 * time.Second, + } + return t, nil +} + type TLSInfo struct { CertFile string KeyFile string From e880dd41f2bab337b4ac991b1ab330ca9f3e983a Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 08:54:24 -0700 Subject: [PATCH 04/16] transport: add TLSInfo.ClientConfig --- transport/listener.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/transport/listener.go b/transport/listener.go index 488bac216..2f1ea7e56 100644 --- a/transport/listener.go +++ b/transport/listener.go @@ -51,21 +51,27 @@ func (info TLSInfo) Empty() bool { return info.CertFile == "" && info.KeyFile == "" } -// Generates a tls.Config object for a server from the given files. -func (info TLSInfo) ServerConfig() (*tls.Config, error) { - // Both the key and cert must be present. +func (info TLSInfo) baseConfig() (*tls.Config, error) { if info.KeyFile == "" || info.CertFile == "" { return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile) } - var cfg tls.Config - tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile) if err != nil { return nil, err } + var cfg tls.Config cfg.Certificates = []tls.Certificate{tlsCert} + return &cfg, nil +} + +// ServerConfig generates a tls.Config object for use by an HTTP server +func (info TLSInfo) ServerConfig() (*tls.Config, error) { + cfg, err := info.baseConfig() + if err != nil { + return nil, err + } if info.CAFile != "" { cfg.ClientAuth = tls.RequireAndVerifyClientCert @@ -78,7 +84,26 @@ func (info TLSInfo) ServerConfig() (*tls.Config, error) { cfg.ClientAuth = tls.NoClientCert } - return &cfg, nil + return cfg, nil +} + +// ClientConfig generates a tls.Config object for use by an HTTP client +func (info TLSInfo) ClientConfig() (*tls.Config, error) { + cfg, err := info.baseConfig() + if err != nil { + return nil, err + } + + if info.CAFile != "" { + cp, err := newCertPool(info.CAFile) + if err != nil { + return nil, err + } + + cfg.RootCAs = cp + } + + return cfg, nil } // newCertPool creates x509 certPool with provided CA file From 342ea182392d71f6763c6b36e34100889318817c Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 08:58:21 -0700 Subject: [PATCH 05/16] transport: build TLS config in NewTransport --- main.go | 2 +- transport/listener.go | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 2cd5cf7ee..94ddb2f23 100644 --- a/main.go +++ b/main.go @@ -151,7 +151,7 @@ func startEtcd() { n = raft.RestartNode(id, peers.IDs(), 10, 1, snapshot, st, ents) } - pt, err := transport.NewTransport() + pt, err := transport.NewTransport(transport.TLSInfo{}) if err != nil { log.Fatal(err) } diff --git a/transport/listener.go b/transport/listener.go index 2f1ea7e56..32e8f281b 100644 --- a/transport/listener.go +++ b/transport/listener.go @@ -29,7 +29,7 @@ func NewListener(addr string, info TLSInfo) (net.Listener, error) { return l, nil } -func NewTransport() (*http.Transport, error) { +func NewTransport(info TLSInfo) (*http.Transport, error) { t := &http.Transport{ // timeouts taken from http.DefaultTransport Dial: (&net.Dialer{ @@ -38,6 +38,15 @@ func NewTransport() (*http.Transport, error) { }).Dial, TLSHandshakeTimeout: 10 * time.Second, } + + if !info.Empty() { + tlsCfg, err := info.ClientConfig() + if err != nil { + return nil, err + } + t.TLSClientConfig = tlsCfg + } + return t, nil } From 27813599a1d345bdca7e957f33b0c71f97367d63 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 08:21:16 -0700 Subject: [PATCH 06/16] etcd: wire up peer TLS flags --- main.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 94ddb2f23..35f4b3716 100644 --- a/main.go +++ b/main.go @@ -51,6 +51,7 @@ var ( } clientTLSInfo = transport.TLSInfo{} + peerTLSInfo = transport.TLSInfo{} ) func init() { @@ -65,6 +66,10 @@ func init() { flag.StringVar(&clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.") flag.StringVar(&clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.") flag.StringVar(&clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.") + + flag.StringVar(&peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.") + flag.StringVar(&peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.") + flag.StringVar(&peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.") } func main() { @@ -151,7 +156,7 @@ func startEtcd() { n = raft.RestartNode(id, peers.IDs(), 10, 1, snapshot, st, ents) } - pt, err := transport.NewTransport(transport.TLSInfo{}) + pt, err := transport.NewTransport(peerTLSInfo) if err != nil { log.Fatal(err) } @@ -179,7 +184,7 @@ func startEtcd() { Info: cors, } - l, err := transport.NewListener(*paddr, transport.TLSInfo{}) + l, err := transport.NewListener(*paddr, peerTLSInfo) if err != nil { log.Fatal(err) } From fb7968d704f1c8ccccb1a73c42e7b455598940a8 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 10:09:34 -0700 Subject: [PATCH 07/16] etcdserver: Peers.Pick returns just an addr --- etcdserver/etcdhttp/peers.go | 12 ++++++------ etcdserver/etcdhttp/peers_test.go | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/etcdserver/etcdhttp/peers.go b/etcdserver/etcdhttp/peers.go index e60e780d6..e95575d29 100644 --- a/etcdserver/etcdhttp/peers.go +++ b/etcdserver/etcdhttp/peers.go @@ -23,14 +23,14 @@ func addScheme(addr string) string { return fmt.Sprintf("http://%s", addr) } -// Pick chooses a random address from a given Peer's addresses, and returns it as -// an addressible URI. If the given peer does not exist, an empty string is returned. +// Pick returns a random address from a given Peer's addresses. If the +// given peer does not exist, an empty string is returned. func (ps Peers) Pick(id int64) string { addrs := ps[id] if len(addrs) == 0 { return "" } - return addScheme(addrs[rand.Intn(len(addrs))]) + return addrs[rand.Intn(len(addrs))] } // Set parses command line sets of names to IPs formatted like: @@ -99,8 +99,8 @@ func Sender(t *http.Transport, p Peers) func(msgs []raftpb.Message) { func send(c *http.Client, p Peers, m raftpb.Message) { // TODO (xiangli): reasonable retry logic for i := 0; i < 3; i++ { - url := p.Pick(m.To) - if url == "" { + addr := p.Pick(m.To) + if addr == "" { // TODO: unknown peer id.. what do we do? I // don't think his should ever happen, need to // look into this further. @@ -108,7 +108,7 @@ func send(c *http.Client, p Peers, m raftpb.Message) { return } - url += raftPrefix + url := fmt.Sprintf("http://%s%s", addr, raftPrefix) // TODO: don't block. we should be able to have 1000s // of messages out at a time. diff --git a/etcdserver/etcdhttp/peers_test.go b/etcdserver/etcdhttp/peers_test.go index e08bc7325..0ed78b762 100644 --- a/etcdserver/etcdhttp/peers_test.go +++ b/etcdserver/etcdhttp/peers_test.go @@ -95,13 +95,13 @@ func TestPeersPick(t *testing.T) { 3: []string{}, } ids := map[string]bool{ - "http://abc": true, - "http://def": true, - "http://ghi": true, - "http://jkl": true, - "http://mno": true, - "http://pqr": true, - "http://stu": true, + "abc": true, + "def": true, + "ghi": true, + "jkl": true, + "mno": true, + "pqr": true, + "stu": true, } for i := 0; i < 1000; i++ { a := ps.Pick(1) @@ -110,8 +110,8 @@ func TestPeersPick(t *testing.T) { break } } - if b := ps.Pick(2); b != "http://xyz" { - t.Errorf("id=%q, want %q", b, "http://xyz") + if b := ps.Pick(2); b != "xyz" { + t.Errorf("id=%q, want %q", b, "xyz") } if c := ps.Pick(3); c != "" { t.Errorf("id=%q, want \"\"", c) From e19b0442f8a7209d723e2a3972aae43a9a82221a Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 10:10:08 -0700 Subject: [PATCH 08/16] etcdserver: pass scheme into send --- etcdserver/etcdhttp/peers.go | 6 +++--- etcdserver/etcdhttp/peers_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/etcdserver/etcdhttp/peers.go b/etcdserver/etcdhttp/peers.go index e95575d29..488b61f8c 100644 --- a/etcdserver/etcdhttp/peers.go +++ b/etcdserver/etcdhttp/peers.go @@ -91,12 +91,12 @@ func Sender(t *http.Transport, p Peers) func(msgs []raftpb.Message) { for _, m := range msgs { // TODO: reuse go routines // limit the number of outgoing connections for the same receiver - go send(c, p, m) + go send(c, "http", p, m) } } } -func send(c *http.Client, p Peers, m raftpb.Message) { +func send(c *http.Client, scheme string, p Peers, m raftpb.Message) { // TODO (xiangli): reasonable retry logic for i := 0; i < 3; i++ { addr := p.Pick(m.To) @@ -108,7 +108,7 @@ func send(c *http.Client, p Peers, m raftpb.Message) { return } - url := fmt.Sprintf("http://%s%s", addr, raftPrefix) + url := fmt.Sprintf("%s://%s%s", scheme, addr, raftPrefix) // TODO: don't block. we should be able to have 1000s // of messages out at a time. diff --git a/etcdserver/etcdhttp/peers_test.go b/etcdserver/etcdhttp/peers_test.go index 0ed78b762..0719e1082 100644 --- a/etcdserver/etcdhttp/peers_test.go +++ b/etcdserver/etcdhttp/peers_test.go @@ -215,7 +215,7 @@ func TestSend(t *testing.T) { ps := Peers{ 42: []string{strings.TrimPrefix(ts.URL, "http://")}, } - send(http.DefaultClient, ps, tt.m) + send(http.DefaultClient, "http", ps, tt.m) if !tt.ok { if tr != nil { From 10220335f780721278aceb9ed6288f3095349525 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 10:10:38 -0700 Subject: [PATCH 09/16] etcdserver: determine scheme based on TLSClientConfig --- etcdserver/etcdhttp/peers.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/etcdserver/etcdhttp/peers.go b/etcdserver/etcdhttp/peers.go index 488b61f8c..8f9e32fd2 100644 --- a/etcdserver/etcdhttp/peers.go +++ b/etcdserver/etcdhttp/peers.go @@ -87,11 +87,17 @@ func (ps Peers) Endpoints() []string { func Sender(t *http.Transport, p Peers) func(msgs []raftpb.Message) { c := &http.Client{Transport: t} + + scheme := "http" + if t.TLSClientConfig != nil { + scheme = "https" + } + return func(msgs []raftpb.Message) { for _, m := range msgs { // TODO: reuse go routines // limit the number of outgoing connections for the same receiver - go send(c, "http", p, m) + go send(c, scheme, p, m) } } } From b94d0281d46d74248d88217dde619529a61087d2 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 10:19:01 -0700 Subject: [PATCH 10/16] etcd: use TLS in proxy transport --- main.go | 8 +++++++- proxy/proxy.go | 16 ++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index 35f4b3716..f9533ebdc 100644 --- a/main.go +++ b/main.go @@ -212,10 +212,16 @@ func startEtcd() { // startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes. func startProxy() { - ph, err := proxy.NewHandler((*peers).Endpoints()) + pt, err := transport.NewTransport(clientTLSInfo) if err != nil { log.Fatal(err) } + + ph, err := proxy.NewHandler(pt, (*peers).Endpoints()) + if err != nil { + log.Fatal(err) + } + ph = &CORSHandler{ Handler: ph, Info: cors, diff --git a/proxy/proxy.go b/proxy/proxy.go index 3dc14a887..9384fd17b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -6,27 +6,15 @@ import ( "time" ) -const ( - dialTimeout = 30 * time.Second - responseHeaderTimeout = 30 * time.Second -) - -func NewHandler(endpoints []string) (http.Handler, error) { +func NewHandler(t *http.Transport, endpoints []string) (http.Handler, error) { d, err := newDirector(endpoints) if err != nil { return nil, err } - tr := http.Transport{ - Dial: func(network, address string) (net.Conn, error) { - return net.DialTimeout(network, address, dialTimeout) - }, - ResponseHeaderTimeout: responseHeaderTimeout, - } - rp := reverseProxy{ director: d, - transport: &tr, + transport: t, } return &rp, nil From 99e9f561eeec162dc732182d488d9c6ce0fa0242 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 10:31:03 -0700 Subject: [PATCH 11/16] etcdserver: add Peers.Addrs --- etcdserver/etcdhttp/peers.go | 13 +++++++++++++ etcdserver/etcdhttp/peers_test.go | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/etcdserver/etcdhttp/peers.go b/etcdserver/etcdhttp/peers.go index 8f9e32fd2..dbe1b4cff 100644 --- a/etcdserver/etcdhttp/peers.go +++ b/etcdserver/etcdhttp/peers.go @@ -85,6 +85,19 @@ func (ps Peers) Endpoints() []string { return endpoints } +// Addrs returns a list of all peer addresses. The returned list is sorted +// in ascending lexicographical order. +func (ps Peers) Addrs() []string { + addrs := make([]string, 0) + for _, paddrs := range ps { + for _, paddr := range paddrs { + addrs = append(addrs, paddr) + } + } + sort.Strings(addrs) + return addrs +} + func Sender(t *http.Transport, p Peers) func(msgs []raftpb.Message) { c := &http.Client{Transport: t} diff --git a/etcdserver/etcdhttp/peers_test.go b/etcdserver/etcdhttp/peers_test.go index 0719e1082..c4f7f04ff 100644 --- a/etcdserver/etcdhttp/peers_test.go +++ b/etcdserver/etcdhttp/peers_test.go @@ -16,24 +16,28 @@ func TestPeers(t *testing.T) { in string wids []int64 wep []string + waddrs []string wstring string }{ { "1=1.1.1.1", []int64{1}, []string{"http://1.1.1.1"}, + []string{"1.1.1.1"}, "1=1.1.1.1", }, { "2=2.2.2.2", []int64{2}, []string{"http://2.2.2.2"}, + []string{"2.2.2.2"}, "2=2.2.2.2", }, { "1=1.1.1.1&1=1.1.1.2&2=2.2.2.2", []int64{1, 2}, []string{"http://1.1.1.1", "http://1.1.1.2", "http://2.2.2.2"}, + []string{"1.1.1.1", "1.1.1.2", "2.2.2.2"}, "1=1.1.1.1&1=1.1.1.2&2=2.2.2.2", }, { @@ -41,6 +45,7 @@ func TestPeers(t *testing.T) { []int64{1, 2, 3, 4}, []string{"http://1.1.1.1", "http://1.1.1.2", "http://2.2.2.2", "http://3.3.3.3", "http://4.4.4.4"}, + []string{"1.1.1.1", "1.1.1.2", "2.2.2.2", "3.3.3.3", "4.4.4.4"}, "1=1.1.1.1&1=1.1.1.2&2=2.2.2.2&3=3.3.3.3&4=4.4.4.4", }, } @@ -59,6 +64,10 @@ func TestPeers(t *testing.T) { if !reflect.DeepEqual(ep, tt.wep) { t.Errorf("#%d: Endpoints=%#v, want %#v", i, ep, tt.wep) } + addrs := p.Addrs() + if !reflect.DeepEqual(addrs, tt.waddrs) { + t.Errorf("#%d: addrs=%#v, want %#v", i, ep, tt.waddrs) + } s := p.String() if s != tt.wstring { t.Errorf("#%d: string=%q, want %q", i, s, tt.wstring) From 1ea3197feb275889f0ab1c3aef5a746eb0c4307b Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 10:31:59 -0700 Subject: [PATCH 12/16] proxy: pass addrs and scheme into newDirector --- main.go | 2 +- proxy/director.go | 27 ++++++--------------- proxy/director_test.go | 54 +++++++++++++++++++++++++++++------------- proxy/proxy.go | 6 ++--- 4 files changed, 47 insertions(+), 42 deletions(-) diff --git a/main.go b/main.go index f9533ebdc..02ac0126c 100644 --- a/main.go +++ b/main.go @@ -217,7 +217,7 @@ func startProxy() { log.Fatal(err) } - ph, err := proxy.NewHandler(pt, (*peers).Endpoints()) + ph, err := proxy.NewHandler(pt, (*peers).Addrs()) if err != nil { log.Fatal(err) } diff --git a/proxy/director.go b/proxy/director.go index 2e124fa05..14c8b5237 100644 --- a/proxy/director.go +++ b/proxy/director.go @@ -2,7 +2,6 @@ package proxy import ( "errors" - "fmt" "log" "net/url" "sync" @@ -15,27 +14,15 @@ const ( endpointFailureWait = 5 * time.Second ) -func newDirector(urls []string) (*director, error) { - if len(urls) == 0 { - return nil, errors.New("one or more endpoints required") +func newDirector(scheme string, addrs []string) (*director, error) { + if len(addrs) == 0 { + return nil, errors.New("one or more upstream addresses required") } - endpoints := make([]*endpoint, len(urls)) - for i, v := range urls { - u, err := url.Parse(v) - if err != nil { - return nil, fmt.Errorf("invalid endpoint %q: %v", v, err) - } - - if u.Scheme == "" { - return nil, fmt.Errorf("invalid endpoint %q: scheme required", v) - } - - if u.Host == "" { - return nil, fmt.Errorf("invalid endpoint %q: host empty", v) - } - - endpoints[i] = newEndpoint(*u) + endpoints := make([]*endpoint, len(addrs)) + for i, addr := range addrs { + u := url.URL{Scheme: scheme, Host: addr} + endpoints[i] = newEndpoint(u) } d := director{ep: endpoints} diff --git a/proxy/director_test.go b/proxy/director_test.go index bd6ce675f..f3614cb3b 100644 --- a/proxy/director_test.go +++ b/proxy/director_test.go @@ -6,29 +6,49 @@ import ( "testing" ) -func TestNewDirectorEndpointValidation(t *testing.T) { +func TestNewDirectorScheme(t *testing.T) { tests := []struct { - good bool - endpoints []string + scheme string + addrs []string + want []string }{ - {true, []string{"http://192.0.2.8"}}, - {true, []string{"http://192.0.2.8:8001"}}, - {true, []string{"http://example.com"}}, - {true, []string{"http://example.com:8001"}}, - {true, []string{"http://192.0.2.8:8001", "http://example.com:8002"}}, + { + scheme: "http", + addrs: []string{"192.0.2.8:4002", "example.com:8080"}, + want: []string{"http://192.0.2.8:4002", "http://example.com:8080"}, + }, + { + scheme: "https", + addrs: []string{"192.0.2.8:4002", "example.com:8080"}, + want: []string{"https://192.0.2.8:4002", "https://example.com:8080"}, + }, - {false, []string{"://"}}, - {false, []string{"http://"}}, - {false, []string{"192.0.2.8"}}, - {false, []string{"192.0.2.8:8001"}}, - {false, []string{""}}, - {false, []string{}}, + // accept addrs without a port + { + scheme: "http", + addrs: []string{"192.0.2.8"}, + want: []string{"http://192.0.2.8"}, + }, + + // accept addrs even if they are garbage + { + scheme: "http", + addrs: []string{"."}, + want: []string{"http://."}, + }, } for i, tt := range tests { - _, err := newDirector(tt.endpoints) - if tt.good != (err == nil) { - t.Errorf("#%d: expected success = %t, got err = %v", i, tt.good, err) + got, err := newDirector(tt.scheme, tt.addrs) + if err != nil { + t.Errorf("#%d: newDirectory returned unexpected error: %v", i, err) + } + + for ii, wep := range tt.want { + gep := got.ep[ii].URL.String() + if !reflect.DeepEqual(wep, gep) { + t.Errorf("#%d: want endpoints[%d] = %#v, got = %#v", i, ii, wep, gep) + } } } } diff --git a/proxy/proxy.go b/proxy/proxy.go index 9384fd17b..a162c5cd0 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1,13 +1,11 @@ package proxy import ( - "net" "net/http" - "time" ) -func NewHandler(t *http.Transport, endpoints []string) (http.Handler, error) { - d, err := newDirector(endpoints) +func NewHandler(t *http.Transport, addrs []string) (http.Handler, error) { + d, err := newDirector("http", addrs) if err != nil { return nil, err } From 73504dca4193faebda444b18486270a7974b4cf3 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 10:32:21 -0700 Subject: [PATCH 13/16] proxy: determine scheme based on TLSClientConfig --- proxy/proxy.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index a162c5cd0..80ae38be9 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -5,7 +5,12 @@ import ( ) func NewHandler(t *http.Transport, addrs []string) (http.Handler, error) { - d, err := newDirector("http", addrs) + scheme := "http" + if t.TLSClientConfig != nil { + scheme = "https" + } + + d, err := newDirector(scheme, addrs) if err != nil { return nil, err } From 3c4b155395fd113e654793138239a1dcdc7e63dd Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 12:14:47 -0700 Subject: [PATCH 14/16] transport: spot-check NewTransport --- transport/listener_test.go | 193 +++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 transport/listener_test.go diff --git a/transport/listener_test.go b/transport/listener_test.go new file mode 100644 index 000000000..209ce95d9 --- /dev/null +++ b/transport/listener_test.go @@ -0,0 +1,193 @@ +package transport + +import ( + "io/ioutil" + "os" + "testing" +) + +var ( + TLSCA = []byte(`-----BEGIN CERTIFICATE----- +MIIFNDCCAx6gAwIBAgIBATALBgkqhkiG9w0BAQUwLTEMMAoGA1UEBhMDVVNBMRAw +DgYDVQQKEwdldGNkLWNhMQswCQYDVQQLEwJDQTAeFw0xNDAzMTMwMjA5MDlaFw0y +NDAzMTMwMjA5MDlaMC0xDDAKBgNVBAYTA1VTQTEQMA4GA1UEChMHZXRjZC1jYTEL +MAkGA1UECxMCQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdlBlw +Jiakc4C1UpMUvQ+2fttyBMfMLivQgj51atpKd8qIBvpZwz1wtpzdRG0hSYMF0IUk +MfBqyg+T5tt2Lfs3Gx3cYKS7G0HTfmABC7GdG8gNvEVNl/efxqvhis7p7hur765e +J+N2GR4oOOP5Wa8O5flv10cp3ZJLhAguc2CONLzfh/iAYAItFgktGHXJ/AnUhhaj +KWdKlK9Cv71YsRPOiB1hCV+LKfNSqrXPMvQ4sarz3yECIBhpV/KfskJoDyeNMaJd +gabX/S7gUCd2FvuOpGWdSIsDwyJf0tnYmQX5XIQwBZJib/IFMmmoVNYc1bFtYvRH +j0g0Ax4tHeXU/0mglqEcaTuMejnx8jlxZAM8Z94wHLfKbtaP0zFwMXkaM4nmfZqh +vLZwowDGMv9M0VRFEhLGYIc3xQ8G2u8cFAGw1UqTxKhwAdRmrcFaQ38sk4kziy0u +AkpGavS7PKcFjjB/fdDFO/kwGQOthX/oTn9nP3BT+IK2h1A6ATMPI4lVnhb5/KBt +9M/fGgbiU+I9QT0Ilz/LlrcCuzyRXREvIZvoUL77Id+JT3qQxqPn/XMKLN4WEFII +112MFGqCD85JZzNoC4RkZd8kFlR4YJWsS4WqJlWprESr5cCDuLviK+31cnIRF4fJ +mz0gPsVgY7GFEan3JJnL8oRUVzdTPKfPt0atsQIDAQABo2MwYTAOBgNVHQ8BAf8E +BAMCAAQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUnVlVvktY+zlLpG43nTpG +AWmUkrYwHwYDVR0jBBgwFoAUnVlVvktY+zlLpG43nTpGAWmUkrYwCwYJKoZIhvcN +AQEFA4ICAQAqIcPFux3V4h1N0aGM4fCS/iT50TzDnRb5hwILKbmyA6LFnH4YF7PZ +aA0utDNo1XSRDMpR38HWk0weh5Sfx6f2danaKZHAsea8oVEtdrz16ZMOvoh0CPIM +/hn0CGQOoXDADDNFASuExhhpoyYkDqTVTCQ/zbhZg1mjBljJ+BBzlSgeoE4rUDpn +nuDcmD9LtjpsVQL+J662rd51xV4Z6a7aZLvN9GfO8tYkfCGCD9+fGh1Cpz0IL7qw +VRie+p/XpjoHemswnRhYJ4wn10a1UkVSR++wld6Gvjb9ikyr9xVyU5yrRM55pP2J +VguhzjhTIDE1eDfIMMxv3Qj8+BdVQwtKFD+zQYQcbcjsvjTErlS7oCbM2DVlPnRT +QaCM0q0yorfzc4hmml5P95ngz2xlohavgNMhsYIlcWyq3NVbm7mIXz2pjqa16Iit +vL7WX6OVupv/EOMRx5cVcLqqEaYJmAlNd/CCD8ihDQCwoJ6DJhczPRexrVp+iZHK +SnIUONdXb/g8ungXUGL1jGNQrWuq49clpI5sLWNjMDMFAQo0qu5bLkOIMlK/evCt +gctOjXDvGXCk5h6Adf14q9zDGFdLoxw0/aciUSn9IekdzYPmkYUTifuzkVRsPKzS +nmI4dQvz0rHIh4FBUKWWrJhRWhrv9ty/YFuJXVUHeAwr5nz6RFZ4wQ== +-----END CERTIFICATE-----`) + TLSKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY-----`) + TLSCert = []byte(`-----BEGIN CERTIFICATE----- +MIIFWzCCA0WgAwIBAgIBAjALBgkqhkiG9w0BAQUwLTEMMAoGA1UEBhMDVVNBMRAw +DgYDVQQKEwdldGNkLWNhMQswCQYDVQQLEwJDQTAeFw0xNDAzMTMwMjA5MjJaFw0y +NDAzMTMwMjA5MjJaMEUxDDAKBgNVBAYTA1VTQTEQMA4GA1UEChMHZXRjZC1jYTEP +MA0GA1UECxMGc2VydmVyMRIwEAYDVQQDEwkxMjcuMC4wLjEwggIiMA0GCSqGSIb3 +DQEBAQUAA4ICDwAwggIKAoICAQDI3EvqJrLWsnPbjAT8ENiMRyBINhhafubi5Nb+ +glEzkbC2kv2zXkVkpkBubDRwyh3eomSbdwKYk3yz+IopT753teJueRpMPq9Ayr/+ +PZl4Y1tG04KcjfOvOls6zPsDfHzluR8TE705If5wwZu3Bdwxzdtx9T0ROzIEgRt0 +Axuce5qkg93IWNxOrIr+4LCxYfTpvpTXO20lz0IuQNm1Opo9PVoWn7PXdOmuCzSG +2hW1DcKqSyQP7IkplBJS0EhoovIsXavSkPKJssvQj73ZFIBVgKhXuHmPNdrypaQk +CtxsqbVdOOlojItqYTTDAiadwRQWkYgDOSQCGJiPqYVJx+rH4MlzxQ6n9x2qIcne +lfMr+VFDEc1YvHu1XLMg5b1ImD6ChutYW0RhFJ3CQVdQR2i4kJ8T1DSJYLISMODZ +ux1cZaUoSL/EkrC5/8POWZmP8nJXO6A4wrZDHF30/qWpo+T5PvsA6cABfX1jkcTx +PBXGK1qOZ8rToTxprJ2zc3zuZNxSgM32nzjcPUgn559Mgdl0HR4c4JeTZGsebWmx +MWmkz//BV4eUaGHqCpzRQHf3YIxysvDC2Xf4z2Alk8AlLRXp7/ksatdxAtyc+y8+ +MWCc6N0YbI9zjv+ezCBqR+mu1P5Tb0HebPFz3dOdIpiC3kU8QyMEagw8u5xliZs4 +AxwdNwIDAQABo3IwcDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYD +VR0OBBYEFD6UrVN8uolWz6et79jVeZetjd4XMB8GA1UdIwQYMBaAFJ1ZVb5LWPs5 +S6RuN506RgFplJK2MA8GA1UdEQQIMAaHBH8AAAEwCwYJKoZIhvcNAQEFA4ICAQCo +sKn1Rjx0tIVWAZAZB4lCWvkQDp/txnb5zzQUlKhIW2o98IklASmOYYyZbE2PXlda +/n8TwKIzWgIoNh5AcgLWhtASrnZdGFXY88n5jGk6CVZ1+Dl+IX99h+r+YHQzf1jU +BjGrZHGv3pPjwhFGDS99lM/TEBk/eLI2Kx5laL+nWMTwa8M1OwSIh6ZxYPVlWUqb +rurk5l/YqW+UkYIXIQhe6LwtB7tBjr6nDIWBfHQ7uN8IdB8VIAF6lejr22VmERTW +j+zJ5eTzuQN1f0s930mEm8pW7KgGxlEqrUlSJtxlMFCv6ZHZk1Y4yEiOCBKlPNme +X3B+lhj//PH3gLNm3+ZRr5ena3k+wL9Dd3d3GDCIx0ERQyrGS/rJpqNPI+8ZQlG0 +nrFlm7aP6UznESQnJoSFbydiD0EZ4hXSdmDdXQkTklRpeXfMcrYBGN7JrGZOZ2T2 +WtXBMx2bgPeEH50KRrwUMFe122bchh0Fr+hGvNK2Q9/gRyQPiYHq6vSF4GzorzLb +aDuWA9JRH8/c0z8tMvJ7KjmmmIxd39WWGZqiBrGQR7utOJjpQl+HCsDIQM6yZ/Bu +RpwKj2yBz0OQg4tWbtqUuFkRMTkCR6vo3PadgO1VWokM7UFUXlScnYswcM5EwnzJ +/IsYJ2s1V706QVUzAGIbi3+wYi3enk7JfYoGIqa2oA== +-----END CERTIFICATE-----`) +) + +func createTempFile(b []byte) (string, error) { + f, err := ioutil.TempFile("", "etcd-test-tls-") + if err != nil { + return "", err + } + defer f.Close() + + if _, err = f.Write(b); err != nil { + return "", err + } + + return f.Name(), nil +} + +func TestNewTransportTLSInfo(t *testing.T) { + fCA, err := createTempFile(TLSCA) + if err != nil { + t.Fatalf("Unable to prepare TLS CA tmpfile: %v", err) + } + defer os.Remove(fCA) + + fCert, err := createTempFile(TLSCert) + if err != nil { + t.Fatalf("Unable to prepare TLS cert tmpfile: %v", err) + } + defer os.Remove(fCert) + + fKey, err := createTempFile(TLSKey) + if err != nil { + t.Fatalf("Unable to prepare TLS key tmpfile: %v", err) + } + defer os.Remove(fKey) + + tests := []struct { + info TLSInfo + wantTLSClientConfig bool + }{ + { + info: TLSInfo{}, + wantTLSClientConfig: false, + }, + { + info: TLSInfo{ + CertFile: fCert, + KeyFile: fKey, + }, + wantTLSClientConfig: true, + }, + { + info: TLSInfo{ + CertFile: fCert, + KeyFile: fKey, + CAFile: fCA, + }, + wantTLSClientConfig: true, + }, + } + + for i, tt := range tests { + trans, err := NewTransport(tt.info) + if err != nil { + t.Fatalf("Received unexpected error from NewTransport: %v", err) + } + + gotTLSClientConfig := trans.TLSClientConfig != nil + if tt.wantTLSClientConfig != gotTLSClientConfig { + t.Fatalf("%#d: wantTLSClientConfig=%t but gotTLSClientConfig=%t", i, tt.wantTLSClientConfig, gotTLSClientConfig) + } + } +} From 6ac4aea2bf0621951e109a15c0770985450775e1 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 12:19:32 -0700 Subject: [PATCH 15/16] transport: test TLSInfo.Empty() --- transport/listener_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/transport/listener_test.go b/transport/listener_test.go index 209ce95d9..e315da157 100644 --- a/transport/listener_test.go +++ b/transport/listener_test.go @@ -191,3 +191,26 @@ func TestNewTransportTLSInfo(t *testing.T) { } } } + +func TestTLSInfoEmpty(t *testing.T) { + tests := []struct { + info TLSInfo + want bool + }{ + {TLSInfo{}, true}, + {TLSInfo{CAFile: "baz"}, true}, + {TLSInfo{CertFile: "foo"}, false}, + {TLSInfo{KeyFile: "bar"}, false}, + {TLSInfo{CertFile: "foo", KeyFile: "bar"}, false}, + {TLSInfo{CertFile: "foo", CAFile: "baz"}, false}, + {TLSInfo{KeyFile: "bar", CAFile: "baz"}, false}, + {TLSInfo{CertFile: "foo", KeyFile: "bar", CAFile: "baz"}, false}, + } + + for i, tt := range tests { + got := tt.info.Empty() + if tt.want != got { + t.Errorf("#%d: result of Empty() incorrect: want=%t got=%t", i, tt.want, got) + } + } +} From 4649a2809729f9be565a07913c9425b5408887cc Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 23 Sep 2014 12:52:59 -0700 Subject: [PATCH 16/16] transport: exercise TLSInfo.ClientConfig & ServerConfig --- transport/listener_test.go | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/transport/listener_test.go b/transport/listener_test.go index e315da157..6447b282f 100644 --- a/transport/listener_test.go +++ b/transport/listener_test.go @@ -1,6 +1,7 @@ package transport import ( + "crypto/tls" "io/ioutil" "os" "testing" @@ -214,3 +215,100 @@ func TestTLSInfoEmpty(t *testing.T) { } } } + +func TestTLSInfoMissingFields(t *testing.T) { + fCA, err := createTempFile(TLSCA) + if err != nil { + t.Fatalf("Unable to prepare TLS CA tmpfile: %v", err) + } + defer os.Remove(fCA) + + fCert, err := createTempFile(TLSCert) + if err != nil { + t.Fatalf("Unable to prepare TLS cert tmpfile: %v", err) + } + defer os.Remove(fCert) + + fKey, err := createTempFile(TLSKey) + if err != nil { + t.Fatalf("Unable to prepare TLS key tmpfile: %v", err) + } + defer os.Remove(fKey) + + tests := []TLSInfo{ + TLSInfo{}, + TLSInfo{CAFile: fCA}, + TLSInfo{CertFile: fCert}, + TLSInfo{KeyFile: fKey}, + TLSInfo{CertFile: fCert, CAFile: fCA}, + TLSInfo{KeyFile: fKey, CAFile: fCA}, + } + + for i, info := range tests { + if _, err := info.ServerConfig(); err == nil { + t.Errorf("#%d: expected non-nil error from ServerConfig()", i) + } + + if _, err = info.ClientConfig(); err == nil { + t.Errorf("#%d: expected non-nil error from ClientConfig()", i) + } + } +} + +func TestTLSInfoConfigFuncs(t *testing.T) { + fCA, err := createTempFile(TLSCA) + if err != nil { + t.Fatalf("Unable to prepare TLS CA tmpfile: %v", err) + } + defer os.Remove(fCA) + + fCert, err := createTempFile(TLSCert) + if err != nil { + t.Fatalf("Unable to prepare TLS cert tmpfile: %v", err) + } + defer os.Remove(fCert) + + fKey, err := createTempFile(TLSKey) + if err != nil { + t.Fatalf("Unable to prepare TLS key tmpfile: %v", err) + } + defer os.Remove(fKey) + + tests := []struct { + info TLSInfo + clientAuth tls.ClientAuthType + wantCAs bool + }{ + { + info: TLSInfo{CertFile: fCert, KeyFile: fKey}, + clientAuth: tls.NoClientCert, + wantCAs: false, + }, + + { + info: TLSInfo{CertFile: fCert, KeyFile: fKey, CAFile: fCA}, + clientAuth: tls.RequireAndVerifyClientCert, + wantCAs: true, + }, + } + + for i, tt := range tests { + sCfg, err := tt.info.ServerConfig() + if err != nil { + t.Errorf("#%d: expected nil error from ServerConfig(), got non-nil: %v", i, err) + } + + if tt.wantCAs != (sCfg.ClientCAs != nil) { + t.Errorf("%#d: wantCAs=%t but ClientCAs=%v", i, tt.wantCAs, sCfg.ClientCAs) + } + + cCfg, err := tt.info.ClientConfig() + if err != nil { + t.Errorf("#%d: expected nil error from ClientConfig(), got non-nil: %v", i, err) + } + + if tt.wantCAs != (cCfg.RootCAs != nil) { + t.Errorf("%#d: wantCAs=%t but RootCAs=%v", i, tt.wantCAs, sCfg.RootCAs) + } + } +}