Configure the master to connect to the kubelet using HTTPS.

This commit is contained in:
Robert Bailey 2015-03-31 14:25:45 -07:00
parent d0f48b68d8
commit 58bc792e68
7 changed files with 84 additions and 67 deletions

View File

@ -99,7 +99,7 @@ func NewAPIServer() *APIServer {
RuntimeConfig: make(util.ConfigurationMap), RuntimeConfig: make(util.ConfigurationMap),
KubeletConfig: client.KubeletConfig{ KubeletConfig: client.KubeletConfig{
Port: 10250, Port: 10250,
EnableHttps: false, EnableHttps: true,
HTTPTimeout: time.Duration(5) * time.Second, HTTPTimeout: time.Duration(5) * time.Second,
}, },
} }

View File

@ -83,7 +83,7 @@ func NewCMServer() *CMServer {
SyncNodeStatus: false, SyncNodeStatus: false,
KubeletConfig: client.KubeletConfig{ KubeletConfig: client.KubeletConfig{
Port: ports.KubeletPort, Port: ports.KubeletPort,
EnableHttps: false, EnableHttps: true,
HTTPTimeout: time.Duration(5) * time.Second, HTTPTimeout: time.Duration(5) * time.Second,
}, },
} }

View File

@ -139,14 +139,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container) error {
// TODO: Convert to go-restful // TODO: Convert to go-restful
func InstallValidator(mux Mux, servers func() map[string]Server) { func InstallValidator(mux Mux, servers func() map[string]Server) {
validator, err := NewValidator(servers) mux.Handle("/validate", NewValidator(servers))
if err != nil {
glog.Errorf("failed to set up validator: %v", err)
return
}
if validator != nil {
mux.Handle("/validate", validator)
}
} }
// TODO: document all handlers // TODO: document all handlers

View File

@ -17,6 +17,7 @@ limitations under the License.
package apiserver package apiserver
import ( import (
"crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -34,21 +35,26 @@ type httpGet interface {
} }
type Server struct { type Server struct {
Addr string Addr string
Port int Port int
Path string Path string
EnableHTTPS bool
} }
// validator is responsible for validating the cluster and serving // validator is responsible for validating the cluster and serving
type validator struct { type validator struct {
// a list of servers to health check // a list of servers to health check
servers func() map[string]Server servers func() map[string]Server
client httpGet rt http.RoundTripper
} }
// TODO: can this use pkg/probe/http // TODO: can this use pkg/probe/http
func (s *Server) check(client httpGet) (probe.Result, string, error) { func (s *Server) check(client httpGet) (probe.Result, string, error) {
resp, err := client.Get("http://" + net.JoinHostPort(s.Addr, strconv.Itoa(s.Port)) + s.Path) scheme := "http://"
if s.EnableHTTPS {
scheme = "https://"
}
resp, err := client.Get(scheme + net.JoinHostPort(s.Addr, strconv.Itoa(s.Port)) + s.Path)
if err != nil { if err != nil {
return probe.Unknown, "", err return probe.Unknown, "", err
} }
@ -81,7 +87,22 @@ func (v *validator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
reply := []ServerStatus{} reply := []ServerStatus{}
for name, server := range v.servers() { for name, server := range v.servers() {
status, msg, err := server.check(v.client) transport := v.rt
if server.EnableHTTPS {
// TODO(roberthbailey): The servers that use HTTPS are currently the
// kubelets, and we should be using a standard kubelet client library
// to talk to them rather than a separate http client.
transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
status, msg, err := server.check(&http.Client{Transport: transport})
var errorMsg string var errorMsg string
if err != nil { if err != nil {
errorMsg = err.Error() errorMsg = err.Error()
@ -103,30 +124,6 @@ func (v *validator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
// NewValidator creates a validator for a set of servers. // NewValidator creates a validator for a set of servers.
func NewValidator(servers func() map[string]Server) (http.Handler, error) { func NewValidator(servers func() map[string]Server) http.Handler {
return &validator{ return &validator{servers: servers, rt: http.DefaultTransport}
servers: servers,
client: &http.Client{},
}, nil
}
func makeTestValidator(servers map[string]string, get httpGet) (http.Handler, error) {
result := map[string]Server{}
for name, value := range servers {
host, port, err := net.SplitHostPort(value)
if err != nil {
return nil, fmt.Errorf("invalid server spec: %s (%v)", value, err)
}
val, err := strconv.Atoi(port)
if err != nil {
return nil, fmt.Errorf("invalid server spec: %s (%v)", port, err)
}
result[name] = Server{Addr: host, Port: val, Path: "/healthz"}
}
v, e := NewValidator(func() map[string]Server { return result })
if e == nil {
v.(*validator).client = get
}
return v, e
} }

View File

@ -21,35 +21,27 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strconv"
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe" "github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
) )
type fakeHttpGet struct { type fakeRoundTripper struct {
err error err error
resp *http.Response resp *http.Response
url string url string
} }
func (f *fakeHttpGet) Get(url string) (*http.Response, error) { func (f *fakeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
f.url = url f.url = req.URL.String()
return f.resp, f.err return f.resp, f.err
} }
func makeFake(data string, statusCode int, err error) *fakeHttpGet {
return &fakeHttpGet{
err: err,
resp: &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(data)),
StatusCode: statusCode,
},
}
}
func TestValidate(t *testing.T) { func TestValidate(t *testing.T) {
tests := []struct { tests := []struct {
err error err error
@ -66,11 +58,18 @@ func TestValidate(t *testing.T) {
s := Server{Addr: "foo.com", Port: 8080, Path: "/healthz"} s := Server{Addr: "foo.com", Port: 8080, Path: "/healthz"}
for _, test := range tests { for _, test := range tests {
fake := makeFake(test.data, test.code, test.err) fakeRT := &fakeRoundTripper{
err: test.err,
resp: &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString(test.data)),
StatusCode: test.code,
},
}
fake := &http.Client{Transport: fakeRT}
status, data, err := s.check(fake) status, data, err := s.check(fake)
expect := fmt.Sprintf("http://%s:%d/healthz", s.Addr, s.Port) expect := fmt.Sprintf("http://%s:%d/healthz", s.Addr, s.Port)
if fake.url != expect { if fakeRT.url != expect {
t.Errorf("expected %s, got %s", expect, fake.url) t.Errorf("expected %s, got %s", expect, fakeRT.url)
} }
if test.expectErr && err == nil { if test.expectErr && err == nil {
t.Errorf("unexpected non-error") t.Errorf("unexpected non-error")
@ -87,8 +86,30 @@ func TestValidate(t *testing.T) {
} }
} }
func makeTestValidator(servers map[string]string, rt http.RoundTripper) (http.Handler, error) {
result := map[string]Server{}
for name, value := range servers {
host, port, err := net.SplitHostPort(value)
if err != nil {
return nil, fmt.Errorf("invalid server spec: %s (%v)", value, err)
}
val, err := strconv.Atoi(port)
if err != nil {
return nil, fmt.Errorf("invalid server spec: %s (%v)", port, err)
}
result[name] = Server{Addr: host, Port: val, Path: "/healthz"}
}
return &validator{servers: func() map[string]Server { return result }, rt: rt}, nil
}
func TestValidator(t *testing.T) { func TestValidator(t *testing.T) {
fake := makeFake("foo", 200, nil) fake := &fakeRoundTripper{
resp: &http.Response{
Body: ioutil.NopCloser(bytes.NewBufferString("foo")),
StatusCode: 200,
},
}
validator, err := makeTestValidator(map[string]string{ validator, err := makeTestValidator(map[string]string{
"foo": "foo.com:80", "foo": "foo.com:80",
"bar": "bar.com:8080", "bar": "bar.com:8080",
@ -101,7 +122,6 @@ func TestValidator(t *testing.T) {
defer testServer.Close() defer testServer.Close()
resp, err := http.Get(testServer.URL + "/validatez") resp, err := http.Get(testServer.URL + "/validatez")
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -113,13 +133,15 @@ func TestValidator(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
status := []ServerStatus{} var status []ServerStatus
err = json.Unmarshal(data, &status) if err := json.Unmarshal(data, &status); err != nil {
if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
components := util.StringSet{} components := util.StringSet{}
for _, s := range status { for _, s := range status {
if s.Err != "nil" {
t.Errorf("Component %v is unhealthy: %v", s.Component, s.Err)
}
components.Insert(s.Component) components.Insert(s.Component)
} }
if len(status) != 2 || !components.Has("foo") || !components.Has("bar") { if len(status) != 2 || !components.Has("foo") || !components.Has("bar") {

View File

@ -75,9 +75,14 @@ type HTTPKubeletClient struct {
func NewKubeletClient(config *KubeletConfig) (KubeletClient, error) { func NewKubeletClient(config *KubeletConfig) (KubeletClient, error) {
transport := http.DefaultTransport transport := http.DefaultTransport
tlsConfig, err := TLSConfigFor(&Config{ cfg := &Config{TLSClientConfig: config.TLSClientConfig}
TLSClientConfig: config.TLSClientConfig, if config.EnableHttps {
}) hasCA := len(config.CAFile) > 0 || len(config.CAData) > 0
if !hasCA {
cfg.Insecure = true
}
}
tlsConfig, err := TLSConfigFor(cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -561,7 +561,7 @@ func (m *Master) getServersToValidate(c *Config) map[string]apiserver.Server {
glog.Errorf("Failed to list minions: %v", err) glog.Errorf("Failed to list minions: %v", err)
} }
for ix, node := range nodes.Items { for ix, node := range nodes.Items {
serversToValidate[fmt.Sprintf("node-%d", ix)] = apiserver.Server{Addr: node.Name, Port: ports.KubeletPort, Path: "/healthz"} serversToValidate[fmt.Sprintf("node-%d", ix)] = apiserver.Server{Addr: node.Name, Port: ports.KubeletPort, Path: "/healthz", EnableHTTPS: true}
} }
return serversToValidate return serversToValidate
} }