From 4181f1b2e149e2aaafd3b57296aff353a1b47053 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Tue, 8 Jul 2014 13:49:22 -0700 Subject: [PATCH] etcd: support raft tls --- etcd/etcd.go | 14 ++++++- etcd/etcd_test.go | 27 +++++++++++-- etcd/transporter.go | 4 +- etcd/v2_http_endpoint_test.go | 6 +-- etcd/v2_http_kv_test.go | 76 +++++++++++++++++------------------ main.go | 26 +++++++++++- 6 files changed, 105 insertions(+), 48 deletions(-) diff --git a/etcd/etcd.go b/etcd/etcd.go index 68053144b..143d50cc6 100644 --- a/etcd/etcd.go +++ b/etcd/etcd.go @@ -1,6 +1,7 @@ package etcd import ( + "crypto/tls" "encoding/json" "fmt" "log" @@ -54,6 +55,17 @@ func New(c *config.Config, id int64) *Server { log.Fatalf("failed sanitizing configuration: %v", err) } + tc := &tls.Config{ + InsecureSkipVerify: true, + } + var err error + if c.PeerTLSInfo().Scheme() == "https" { + tc, err = c.PeerTLSInfo().ClientConfig() + if err != nil { + log.Fatal("failed to create raft transporter tls:", err) + } + } + s := &Server{ config: c, id: id, @@ -66,7 +78,7 @@ func New(c *config.Config, id int64) *Server { Node: raft.New(id, defaultHeartbeat, defaultElection), result: make(map[wait]chan interface{}), }, - t: newTransporter(), + t: newTransporter(tc), Store: store.New(), diff --git a/etcd/etcd_test.go b/etcd/etcd_test.go index 68ba9257c..6bc376d3f 100644 --- a/etcd/etcd_test.go +++ b/etcd/etcd_test.go @@ -14,7 +14,7 @@ func TestMultipleNodes(t *testing.T) { tests := []int{1, 3, 5, 9, 11} for _, tt := range tests { - es, hs := buildCluster(tt) + es, hs := buildCluster(tt, false) waitCluster(t, es) for i := range es { es[len(es)-i-1].Stop() @@ -26,7 +26,23 @@ func TestMultipleNodes(t *testing.T) { afterTest(t) } -func buildCluster(number int) ([]*Server, []*httptest.Server) { +func TestMultipleTLSNodes(t *testing.T) { + tests := []int{1, 3, 5} + + for _, tt := range tests { + es, hs := buildCluster(tt, true) + waitCluster(t, es) + for i := range es { + es[len(es)-i-1].Stop() + } + for i := range hs { + hs[len(hs)-i-1].Close() + } + } + afterTest(t) +} + +func buildCluster(number int, tls bool) ([]*Server, []*httptest.Server) { bootstrapper := 0 es := make([]*Server, number) hs := make([]*httptest.Server, number) @@ -42,7 +58,12 @@ func buildCluster(number int) ([]*Server, []*httptest.Server) { m.Handle("/raft", es[i].t) m.Handle("/raft/", es[i].t) - hs[i] = httptest.NewServer(m) + if tls { + hs[i] = httptest.NewTLSServer(m) + } else { + hs[i] = httptest.NewServer(m) + } + es[i].raftPubAddr = hs[i].URL es[i].pubAddr = hs[i].URL diff --git a/etcd/transporter.go b/etcd/transporter.go index 39a755c2d..31cd11c90 100644 --- a/etcd/transporter.go +++ b/etcd/transporter.go @@ -2,6 +2,7 @@ package etcd import ( "bytes" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -31,8 +32,9 @@ type transporter struct { *http.ServeMux } -func newTransporter() *transporter { +func newTransporter(tc *tls.Config) *transporter { tr := new(http.Transport) + tr.TLSClientConfig = tc c := &http.Client{Transport: tr} t := &transporter{ diff --git a/etcd/v2_http_endpoint_test.go b/etcd/v2_http_endpoint_test.go index b8381dd34..663172d64 100644 --- a/etcd/v2_http_endpoint_test.go +++ b/etcd/v2_http_endpoint_test.go @@ -13,7 +13,7 @@ import ( ) func TestMachinesEndPoint(t *testing.T) { - es, hs := buildCluster(3) + es, hs := buildCluster(3, false) waitCluster(t, es) w := make([]string, len(hs)) @@ -50,7 +50,7 @@ func TestMachinesEndPoint(t *testing.T) { } func TestLeaderEndPoint(t *testing.T) { - es, hs := buildCluster(3) + es, hs := buildCluster(3, false) waitCluster(t, es) us := make([]string, len(hs)) @@ -87,7 +87,7 @@ func TestLeaderEndPoint(t *testing.T) { } func TestStoreStatsEndPoint(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) waitCluster(t, es) resp, err := http.Get(hs[0].URL + v2StoreStatsPrefix) diff --git a/etcd/v2_http_kv_test.go b/etcd/v2_http_kv_test.go index 390681a31..8228595c2 100644 --- a/etcd/v2_http_kv_test.go +++ b/etcd/v2_http_kv_test.go @@ -17,7 +17,7 @@ import ( // $ curl -X PUT localhost:4001/v2/keys/foo/bar?dir=true // func TestV2SetDirectory(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL resp, err := PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo?dir=true"), url.Values{}) assert.Equal(t, resp.StatusCode, http.StatusCreated) @@ -34,7 +34,7 @@ func TestV2SetDirectory(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d ttl=20 // func TestV2SetKeyWithTTL(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL t0 := time.Now() v := url.Values{} @@ -59,7 +59,7 @@ func TestV2SetKeyWithTTL(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d ttl=bad_ttl // func TestV2SetKeyWithBadTTL(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -80,7 +80,7 @@ func TestV2SetKeyWithBadTTL(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false // func TestV2CreateKeySuccess(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -101,7 +101,7 @@ func TestV2CreateKeySuccess(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=false -> fail // func TestV2CreateKeyFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -127,7 +127,7 @@ func TestV2CreateKeyFail(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevExist=true // func TestV2UpdateKeySuccess(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -154,7 +154,7 @@ func TestV2UpdateKeySuccess(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevExist=true // func TestV2UpdateKeyFailOnValue(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} resp, _ := PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo?dir=true"), v) @@ -180,7 +180,7 @@ func TestV2UpdateKeyFailOnValue(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevExist=true -> fail // func TestV2UpdateKeyFailOnMissingDirectory(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "YYY") @@ -210,7 +210,7 @@ func TestV2UpdateKeyFailOnMissingDirectory(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo -d value=XXX -d ttl= -d prevExist=true // func TestV2UpdateKeySuccessWithTTL(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -248,7 +248,7 @@ func TestV2UpdateKeySuccessWithTTL(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=1 // func TestV2SetKeyCASOnIndexSuccess(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -277,7 +277,7 @@ func TestV2SetKeyCASOnIndexSuccess(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=10 // func TestV2SetKeyCASOnIndexFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -304,7 +304,7 @@ func TestV2SetKeyCASOnIndexFail(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevIndex=bad_index // func TestV2SetKeyCASWithInvalidIndex(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "YYY") @@ -326,7 +326,7 @@ func TestV2SetKeyCASWithInvalidIndex(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=XXX // func TestV2SetKeyCASOnValueSuccess(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -354,7 +354,7 @@ func TestV2SetKeyCASOnValueSuccess(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA // func TestV2SetKeyCASOnValueFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -381,7 +381,7 @@ func TestV2SetKeyCASOnValueFail(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX -d prevValue= // func TestV2SetKeyCASWithMissingValueFails(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -403,7 +403,7 @@ func TestV2SetKeyCASWithMissingValueFails(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA -d prevIndex=4 // func TestV2SetKeyCASOnValueAndIndexFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -432,7 +432,7 @@ func TestV2SetKeyCASOnValueAndIndexFail(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=XXX -d prevIndex=4 // func TestV2SetKeyCASOnValueMatchAndIndexFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -461,7 +461,7 @@ func TestV2SetKeyCASOnValueMatchAndIndexFail(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY -d prevValue=AAA -d prevIndex=3 // func TestV2SetKeyCASOnIndexMatchAndValueFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "XXX") @@ -489,7 +489,7 @@ func TestV2SetKeyCASOnIndexMatchAndValueFail(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value= // func TestV2SetKeyCASWithEmptyValueSuccess(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} v.Set("value", "") @@ -503,7 +503,7 @@ func TestV2SetKeyCASWithEmptyValueSuccess(t *testing.T) { } func TestV2SetKey(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -521,7 +521,7 @@ func TestV2SetKey(t *testing.T) { } func TestV2SetKeyRedirect(t *testing.T) { - es, hs := buildCluster(3) + es, hs := buildCluster(3, false) waitCluster(t, es) u := hs[1].URL ru := fmt.Sprintf("%s%s", hs[0].URL, "/v2/keys/foo/bar") @@ -555,7 +555,7 @@ func TestV2SetKeyRedirect(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo/bar // func TestV2DeleteKey(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -583,7 +583,7 @@ func TestV2DeleteKey(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo?dir=true // func TestV2DeleteEmptyDirectory(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL resp, err := PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo?dir=true"), url.Values{}) @@ -611,7 +611,7 @@ func TestV2DeleteEmptyDirectory(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo?dir=true&recursive=true // func TestV2DeleteNonEmptyDirectory(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL resp, err := PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo/bar?dir=true"), url.Values{}) @@ -637,7 +637,7 @@ func TestV2DeleteNonEmptyDirectory(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo?recursive=true // func TestV2DeleteDirectoryRecursiveImpliesDir(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL resp, err := PutForm(fmt.Sprintf("%s%s", u, "/v2/keys/foo?dir=true"), url.Values{}) @@ -659,7 +659,7 @@ func TestV2DeleteDirectoryRecursiveImpliesDir(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo?prevIndex=3 // func TestV2DeleteKeyCADOnIndexSuccess(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -686,7 +686,7 @@ func TestV2DeleteKeyCADOnIndexSuccess(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo?prevIndex=100 // func TestV2DeleteKeyCADOnIndexFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -709,7 +709,7 @@ func TestV2DeleteKeyCADOnIndexFail(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex=bad_index // func TestV2DeleteKeyCADWithInvalidIndex(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -731,7 +731,7 @@ func TestV2DeleteKeyCADWithInvalidIndex(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevValue=XXX // func TestV2DeleteKeyCADOnValueSuccess(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -756,7 +756,7 @@ func TestV2DeleteKeyCADOnValueSuccess(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevValue=YYY // func TestV2DeleteKeyCADOnValueFail(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -778,7 +778,7 @@ func TestV2DeleteKeyCADOnValueFail(t *testing.T) { // $ curl -X DELETE localhost:4001/v2/keys/foo/bar?prevIndex= // func TestV2DeleteKeyCADWithInvalidValue(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -801,7 +801,7 @@ func TestV2DeleteKeyCADWithInvalidValue(t *testing.T) { // $ curl -X POST localhost:4001/v2/keys/foo/baz // func TestV2CreateUnique(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL // POST should add index to list. @@ -843,7 +843,7 @@ func TestV2CreateUnique(t *testing.T) { // $ curl localhost:4001/v2/keys/foo/bar // func TestV2GetKey(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -878,7 +878,7 @@ func TestV2GetKey(t *testing.T) { // $ curl localhost:4001/v2/keys/foo -d recursive=true // func TestV2GetKeyRecursively(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} @@ -927,7 +927,7 @@ func TestV2GetKeyRecursively(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=XXX // func TestV2WatchKey(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL // There exists a little gap between etcd ready to serve and @@ -985,7 +985,7 @@ func TestV2WatchKey(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/foo/bar -d value=YYY // func TestV2WatchKeyWithIndex(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL var body map[string]interface{} @@ -1044,7 +1044,7 @@ func TestV2WatchKeyWithIndex(t *testing.T) { // $ curl -X PUT localhost:4001/v2/keys/keyindir/bar -d value=YYY // func TestV2WatchKeyInDir(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL var body map[string]interface{} @@ -1096,7 +1096,7 @@ func TestV2WatchKeyInDir(t *testing.T) { // $ curl -I localhost:4001/v2/keys/foo/bar // func TestV2HeadKey(t *testing.T) { - es, hs := buildCluster(1) + es, hs := buildCluster(1, false) u := hs[0].URL v := url.Values{} diff --git a/main.go b/main.go index ce5cb2fe2..01c6923af 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,11 @@ package main import ( + "crypto/tls" "fmt" "log" "math/rand" + "net" "net/http" "os" "time" @@ -25,12 +27,32 @@ func main() { } e := etcd.New(config, genId()) + rTLS, rerr := config.PeerTLSInfo().ServerConfig() + go e.Run() go func() { - if err := http.ListenAndServe(config.Peer.BindAddr, e.RaftHandler()); err != nil { - log.Fatal("system", err) + l, err := net.Listen("tcp", config.Peer.BindAddr) + if err != nil { + log.Fatal(err) } + log.Println("raft server starts listening on", config.Peer.BindAddr) + + switch config.PeerTLSInfo().Scheme() { + case "http": + log.Println("raft server starts serving HTTP") + + case "https": + if rTLS == nil { + log.Fatal("failed to create raft tls:", rerr) + } + l = tls.NewListener(l, rTLS) + log.Println("raft server starts serving HTTPS") + default: + log.Fatal("unsupported http scheme", config.PeerTLSInfo().Scheme()) + } + + log.Fatal(http.Serve(l, e.RaftHandler())) }() if err := http.ListenAndServe(config.BindAddr, e); err != nil {