mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #1142 from bcwaldon/TLS
TLS: peer server/transport and proxy transport
This commit is contained in:
commit
4d68c933d1
@ -23,14 +23,14 @@ func addScheme(addr string) string {
|
|||||||
return fmt.Sprintf("http://%s", addr)
|
return fmt.Sprintf("http://%s", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick chooses a random address from a given Peer's addresses, and returns it as
|
// Pick returns a random address from a given Peer's addresses. If the
|
||||||
// an addressible URI. If the given peer does not exist, an empty string is returned.
|
// given peer does not exist, an empty string is returned.
|
||||||
func (ps Peers) Pick(id int64) string {
|
func (ps Peers) Pick(id int64) string {
|
||||||
addrs := ps[id]
|
addrs := ps[id]
|
||||||
if len(addrs) == 0 {
|
if len(addrs) == 0 {
|
||||||
return ""
|
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:
|
// Set parses command line sets of names to IPs formatted like:
|
||||||
@ -85,21 +85,41 @@ func (ps Peers) Endpoints() []string {
|
|||||||
return endpoints
|
return endpoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func Sender(p Peers) func(msgs []raftpb.Message) {
|
// 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}
|
||||||
|
|
||||||
|
scheme := "http"
|
||||||
|
if t.TLSClientConfig != nil {
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
|
|
||||||
return func(msgs []raftpb.Message) {
|
return func(msgs []raftpb.Message) {
|
||||||
for _, m := range msgs {
|
for _, m := range msgs {
|
||||||
// TODO: reuse go routines
|
// TODO: reuse go routines
|
||||||
// limit the number of outgoing connections for the same receiver
|
// limit the number of outgoing connections for the same receiver
|
||||||
go send(p, m)
|
go send(c, scheme, p, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func send(p Peers, m raftpb.Message) {
|
func send(c *http.Client, scheme string, p Peers, m raftpb.Message) {
|
||||||
// TODO (xiangli): reasonable retry logic
|
// TODO (xiangli): reasonable retry logic
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
url := p.Pick(m.To)
|
addr := p.Pick(m.To)
|
||||||
if url == "" {
|
if addr == "" {
|
||||||
// TODO: unknown peer id.. what do we do? I
|
// TODO: unknown peer id.. what do we do? I
|
||||||
// don't think his should ever happen, need to
|
// don't think his should ever happen, need to
|
||||||
// look into this further.
|
// look into this further.
|
||||||
@ -107,7 +127,7 @@ func send(p Peers, m raftpb.Message) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url += raftPrefix
|
url := fmt.Sprintf("%s://%s%s", scheme, addr, raftPrefix)
|
||||||
|
|
||||||
// TODO: don't block. we should be able to have 1000s
|
// TODO: don't block. we should be able to have 1000s
|
||||||
// of messages out at a time.
|
// of messages out at a time.
|
||||||
@ -116,16 +136,15 @@ func send(p Peers, m raftpb.Message) {
|
|||||||
log.Println("etcdhttp: dropping message:", err)
|
log.Println("etcdhttp: dropping message:", err)
|
||||||
return // drop bad message
|
return // drop bad message
|
||||||
}
|
}
|
||||||
if httpPost(url, data) {
|
if httpPost(c, url, data) {
|
||||||
return // success
|
return // success
|
||||||
}
|
}
|
||||||
// TODO: backoff
|
// TODO: backoff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpPost(url string, data []byte) bool {
|
func httpPost(c *http.Client, url string, data []byte) bool {
|
||||||
// TODO: set timeouts
|
resp, err := c.Post(url, "application/protobuf", bytes.NewBuffer(data))
|
||||||
resp, err := http.Post(url, "application/protobuf", bytes.NewBuffer(data))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elog.TODO()
|
elog.TODO()
|
||||||
return false
|
return false
|
||||||
|
@ -16,24 +16,28 @@ func TestPeers(t *testing.T) {
|
|||||||
in string
|
in string
|
||||||
wids []int64
|
wids []int64
|
||||||
wep []string
|
wep []string
|
||||||
|
waddrs []string
|
||||||
wstring string
|
wstring string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"1=1.1.1.1",
|
"1=1.1.1.1",
|
||||||
[]int64{1},
|
[]int64{1},
|
||||||
[]string{"http://1.1.1.1"},
|
[]string{"http://1.1.1.1"},
|
||||||
|
[]string{"1.1.1.1"},
|
||||||
"1=1.1.1.1",
|
"1=1.1.1.1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"2=2.2.2.2",
|
"2=2.2.2.2",
|
||||||
[]int64{2},
|
[]int64{2},
|
||||||
[]string{"http://2.2.2.2"},
|
[]string{"http://2.2.2.2"},
|
||||||
|
[]string{"2.2.2.2"},
|
||||||
"2=2.2.2.2",
|
"2=2.2.2.2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"1=1.1.1.1&1=1.1.1.2&2=2.2.2.2",
|
"1=1.1.1.1&1=1.1.1.2&2=2.2.2.2",
|
||||||
[]int64{1, 2},
|
[]int64{1, 2},
|
||||||
[]string{"http://1.1.1.1", "http://1.1.1.2", "http://2.2.2.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",
|
"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},
|
[]int64{1, 2, 3, 4},
|
||||||
[]string{"http://1.1.1.1", "http://1.1.1.2", "http://2.2.2.2",
|
[]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"},
|
"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",
|
"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) {
|
if !reflect.DeepEqual(ep, tt.wep) {
|
||||||
t.Errorf("#%d: Endpoints=%#v, want %#v", i, 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()
|
s := p.String()
|
||||||
if s != tt.wstring {
|
if s != tt.wstring {
|
||||||
t.Errorf("#%d: string=%q, want %q", i, s, tt.wstring)
|
t.Errorf("#%d: string=%q, want %q", i, s, tt.wstring)
|
||||||
@ -95,13 +104,13 @@ func TestPeersPick(t *testing.T) {
|
|||||||
3: []string{},
|
3: []string{},
|
||||||
}
|
}
|
||||||
ids := map[string]bool{
|
ids := map[string]bool{
|
||||||
"http://abc": true,
|
"abc": true,
|
||||||
"http://def": true,
|
"def": true,
|
||||||
"http://ghi": true,
|
"ghi": true,
|
||||||
"http://jkl": true,
|
"jkl": true,
|
||||||
"http://mno": true,
|
"mno": true,
|
||||||
"http://pqr": true,
|
"pqr": true,
|
||||||
"http://stu": true,
|
"stu": true,
|
||||||
}
|
}
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
a := ps.Pick(1)
|
a := ps.Pick(1)
|
||||||
@ -110,8 +119,8 @@ func TestPeersPick(t *testing.T) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if b := ps.Pick(2); b != "http://xyz" {
|
if b := ps.Pick(2); b != "xyz" {
|
||||||
t.Errorf("id=%q, want %q", b, "http://xyz")
|
t.Errorf("id=%q, want %q", b, "xyz")
|
||||||
}
|
}
|
||||||
if c := ps.Pick(3); c != "" {
|
if c := ps.Pick(3); c != "" {
|
||||||
t.Errorf("id=%q, want \"\"", c)
|
t.Errorf("id=%q, want \"\"", c)
|
||||||
@ -148,7 +157,7 @@ func TestHttpPost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
ts := httptest.NewServer(tt.h)
|
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)
|
t.Errorf("#%d: httpPost()=%t, want %t", i, g, tt.w)
|
||||||
}
|
}
|
||||||
if tr.Method != "POST" {
|
if tr.Method != "POST" {
|
||||||
@ -161,7 +170,7 @@ func TestHttpPost(t *testing.T) {
|
|||||||
ts.Close()
|
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!")
|
t.Errorf("httpPost with bad URL returned true unexpectedly!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,7 +224,7 @@ func TestSend(t *testing.T) {
|
|||||||
ps := Peers{
|
ps := Peers{
|
||||||
42: []string{strings.TrimPrefix(ts.URL, "http://")},
|
42: []string{strings.TrimPrefix(ts.URL, "http://")},
|
||||||
}
|
}
|
||||||
send(ps, tt.m)
|
send(http.DefaultClient, "http", ps, tt.m)
|
||||||
|
|
||||||
if !tt.ok {
|
if !tt.ok {
|
||||||
if tr != nil {
|
if tr != nil {
|
||||||
|
22
main.go
22
main.go
@ -51,6 +51,7 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
clientTLSInfo = transport.TLSInfo{}
|
clientTLSInfo = transport.TLSInfo{}
|
||||||
|
peerTLSInfo = transport.TLSInfo{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
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.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.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(&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() {
|
func main() {
|
||||||
@ -151,6 +156,11 @@ func startEtcd() {
|
|||||||
n = raft.RestartNode(id, peers.IDs(), 10, 1, snapshot, st, ents)
|
n = raft.RestartNode(id, peers.IDs(), 10, 1, snapshot, st, ents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pt, err := transport.NewTransport(peerTLSInfo)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
s := &etcdserver.EtcdServer{
|
s := &etcdserver.EtcdServer{
|
||||||
Store: st,
|
Store: st,
|
||||||
Node: n,
|
Node: n,
|
||||||
@ -158,7 +168,7 @@ func startEtcd() {
|
|||||||
*wal.WAL
|
*wal.WAL
|
||||||
*snap.Snapshotter
|
*snap.Snapshotter
|
||||||
}{w, snapshotter},
|
}{w, snapshotter},
|
||||||
Send: etcdhttp.Sender(*peers),
|
Send: etcdhttp.Sender(pt, *peers),
|
||||||
Ticker: time.Tick(100 * time.Millisecond),
|
Ticker: time.Tick(100 * time.Millisecond),
|
||||||
SyncTicker: time.Tick(500 * time.Millisecond),
|
SyncTicker: time.Tick(500 * time.Millisecond),
|
||||||
SnapCount: *snapCount,
|
SnapCount: *snapCount,
|
||||||
@ -174,7 +184,7 @@ func startEtcd() {
|
|||||||
Info: cors,
|
Info: cors,
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := transport.NewListener(*paddr, transport.TLSInfo{})
|
l, err := transport.NewListener(*paddr, peerTLSInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -202,10 +212,16 @@ func startEtcd() {
|
|||||||
|
|
||||||
// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
|
// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
|
||||||
func startProxy() {
|
func startProxy() {
|
||||||
ph, err := proxy.NewHandler((*peers).Endpoints())
|
pt, err := transport.NewTransport(clientTLSInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ph, err := proxy.NewHandler(pt, (*peers).Addrs())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
ph = &CORSHandler{
|
ph = &CORSHandler{
|
||||||
Handler: ph,
|
Handler: ph,
|
||||||
Info: cors,
|
Info: cors,
|
||||||
|
@ -2,7 +2,6 @@ package proxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
@ -15,27 +14,15 @@ const (
|
|||||||
endpointFailureWait = 5 * time.Second
|
endpointFailureWait = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func newDirector(urls []string) (*director, error) {
|
func newDirector(scheme string, addrs []string) (*director, error) {
|
||||||
if len(urls) == 0 {
|
if len(addrs) == 0 {
|
||||||
return nil, errors.New("one or more endpoints required")
|
return nil, errors.New("one or more upstream addresses required")
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints := make([]*endpoint, len(urls))
|
endpoints := make([]*endpoint, len(addrs))
|
||||||
for i, v := range urls {
|
for i, addr := range addrs {
|
||||||
u, err := url.Parse(v)
|
u := url.URL{Scheme: scheme, Host: addr}
|
||||||
if err != nil {
|
endpoints[i] = newEndpoint(u)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d := director{ep: endpoints}
|
d := director{ep: endpoints}
|
||||||
|
@ -6,29 +6,49 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewDirectorEndpointValidation(t *testing.T) {
|
func TestNewDirectorScheme(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
good bool
|
scheme string
|
||||||
endpoints []string
|
addrs []string
|
||||||
|
want []string
|
||||||
}{
|
}{
|
||||||
{true, []string{"http://192.0.2.8"}},
|
{
|
||||||
{true, []string{"http://192.0.2.8:8001"}},
|
scheme: "http",
|
||||||
{true, []string{"http://example.com"}},
|
addrs: []string{"192.0.2.8:4002", "example.com:8080"},
|
||||||
{true, []string{"http://example.com:8001"}},
|
want: []string{"http://192.0.2.8:4002", "http://example.com:8080"},
|
||||||
{true, []string{"http://192.0.2.8:8001", "http://example.com:8002"}},
|
},
|
||||||
|
{
|
||||||
|
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{"://"}},
|
// accept addrs without a port
|
||||||
{false, []string{"http://"}},
|
{
|
||||||
{false, []string{"192.0.2.8"}},
|
scheme: "http",
|
||||||
{false, []string{"192.0.2.8:8001"}},
|
addrs: []string{"192.0.2.8"},
|
||||||
{false, []string{""}},
|
want: []string{"http://192.0.2.8"},
|
||||||
{false, []string{}},
|
},
|
||||||
|
|
||||||
|
// accept addrs even if they are garbage
|
||||||
|
{
|
||||||
|
scheme: "http",
|
||||||
|
addrs: []string{"."},
|
||||||
|
want: []string{"http://."},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
_, err := newDirector(tt.endpoints)
|
got, err := newDirector(tt.scheme, tt.addrs)
|
||||||
if tt.good != (err == nil) {
|
if err != nil {
|
||||||
t.Errorf("#%d: expected success = %t, got err = %v", i, tt.good, err)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,23 @@
|
|||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
func NewHandler(t *http.Transport, addrs []string) (http.Handler, error) {
|
||||||
dialTimeout = 30 * time.Second
|
scheme := "http"
|
||||||
responseHeaderTimeout = 30 * time.Second
|
if t.TLSClientConfig != nil {
|
||||||
)
|
scheme = "https"
|
||||||
|
}
|
||||||
|
|
||||||
func NewHandler(endpoints []string) (http.Handler, error) {
|
d, err := newDirector(scheme, addrs)
|
||||||
d, err := newDirector(endpoints)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := http.Transport{
|
|
||||||
Dial: func(network, address string) (net.Conn, error) {
|
|
||||||
return net.DialTimeout(network, address, dialTimeout)
|
|
||||||
},
|
|
||||||
ResponseHeaderTimeout: responseHeaderTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
rp := reverseProxy{
|
rp := reverseProxy{
|
||||||
director: d,
|
director: d,
|
||||||
transport: &tr,
|
transport: t,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &rp, nil
|
return &rp, nil
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewListener(addr string, info TLSInfo) (net.Listener, error) {
|
func NewListener(addr string, info TLSInfo) (net.Listener, error) {
|
||||||
@ -27,6 +29,27 @@ func NewListener(addr string, info TLSInfo) (net.Listener, error) {
|
|||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewTransport(info TLSInfo) (*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,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.Empty() {
|
||||||
|
tlsCfg, err := info.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.TLSClientConfig = tlsCfg
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
type TLSInfo struct {
|
type TLSInfo struct {
|
||||||
CertFile string
|
CertFile string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
@ -37,21 +60,27 @@ func (info TLSInfo) Empty() bool {
|
|||||||
return info.CertFile == "" && info.KeyFile == ""
|
return info.CertFile == "" && info.KeyFile == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates a tls.Config object for a server from the given files.
|
func (info TLSInfo) baseConfig() (*tls.Config, error) {
|
||||||
func (info TLSInfo) ServerConfig() (*tls.Config, error) {
|
|
||||||
// Both the key and cert must be present.
|
|
||||||
if info.KeyFile == "" || info.CertFile == "" {
|
if info.KeyFile == "" || info.CertFile == "" {
|
||||||
return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", 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)
|
tlsCert, err := tls.LoadX509KeyPair(info.CertFile, info.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cfg tls.Config
|
||||||
cfg.Certificates = []tls.Certificate{tlsCert}
|
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 != "" {
|
if info.CAFile != "" {
|
||||||
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
cfg.ClientAuth = tls.RequireAndVerifyClientCert
|
||||||
@ -59,14 +88,31 @@ func (info TLSInfo) ServerConfig() (*tls.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.RootCAs = cp
|
|
||||||
cfg.ClientCAs = cp
|
cfg.ClientCAs = cp
|
||||||
} else {
|
} else {
|
||||||
cfg.ClientAuth = tls.NoClientCert
|
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
|
// newCertPool creates x509 certPool with provided CA file
|
||||||
|
314
transport/listener_test.go
Normal file
314
transport/listener_test.go
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TLSCA = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----`)
|
||||||
|
TLSKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
-----END RSA PRIVATE KEY-----`)
|
||||||
|
TLSCert = []byte(`-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user