add insecurebackendproxy
This commit is contained in:
parent
a886247fe4
commit
867ee1d5ff
@ -4203,6 +4203,15 @@ type PodLogOptions struct {
|
||||
// log output. This may not display a complete final line of logging, and may return
|
||||
// slightly more or slightly less than the specified limit.
|
||||
LimitBytes *int64
|
||||
|
||||
// insecureSkipTLSVerifyBackend indicates that the apiserver should not confirm the validity of the
|
||||
// serving certificate of the backend it is connecting to. This will make the HTTPS connection between the apiserver
|
||||
// and the backend insecure. This means the apiserver cannot verify the log data it is receiving came from the real
|
||||
// kubelet. If the kubelet is configured to verify the apiserver's TLS credentials, it does not mean the
|
||||
// connection to the real kubelet is vulnerable to a man in the middle attack (e.g. an attacker could not intercept
|
||||
// the actual log data coming from the real kubelet).
|
||||
// +optional
|
||||
InsecureSkipTLSVerifyBackend bool
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
@ -484,6 +484,12 @@ const (
|
||||
//
|
||||
// Enables the startupProbe in kubelet worker.
|
||||
StartupProbe featuregate.Feature = "StartupProbe"
|
||||
|
||||
// owner: @deads2k
|
||||
// beta: v1.17
|
||||
//
|
||||
// Enables the users to skip TLS verification of kubelets on pod logs requests
|
||||
AllowInsecureBackendProxy featuregate.Feature = "AllowInsecureBackendProxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -563,6 +569,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
EndpointSlice: {Default: false, PreRelease: featuregate.Alpha},
|
||||
EvenPodsSpread: {Default: false, PreRelease: featuregate.Alpha},
|
||||
StartupProbe: {Default: false, PreRelease: featuregate.Alpha},
|
||||
AllowInsecureBackendProxy: {Default: true, PreRelease: featuregate.Beta},
|
||||
|
||||
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
|
||||
// unintentionally on either side:
|
||||
|
@ -65,10 +65,11 @@ type KubeletClientConfig struct {
|
||||
|
||||
// ConnectionInfo provides the information needed to connect to a kubelet
|
||||
type ConnectionInfo struct {
|
||||
Scheme string
|
||||
Hostname string
|
||||
Port string
|
||||
Transport http.RoundTripper
|
||||
Scheme string
|
||||
Hostname string
|
||||
Port string
|
||||
Transport http.RoundTripper
|
||||
InsecureSkipTLSVerifyTransport http.RoundTripper
|
||||
}
|
||||
|
||||
// ConnectionInfoGetter provides ConnectionInfo for the kubelet running on a named node
|
||||
@ -76,9 +77,28 @@ type ConnectionInfoGetter interface {
|
||||
GetConnectionInfo(ctx context.Context, nodeName types.NodeName) (*ConnectionInfo, error)
|
||||
}
|
||||
|
||||
// MakeTransport creates a RoundTripper for HTTP Transport.
|
||||
// MakeTransport creates a secure RoundTripper for HTTP Transport.
|
||||
func MakeTransport(config *KubeletClientConfig) (http.RoundTripper, error) {
|
||||
tlsConfig, err := transport.TLSConfigFor(config.transportConfig())
|
||||
return makeTransport(config, false)
|
||||
}
|
||||
|
||||
// MakeInsecureTransport creates an insecure RoundTripper for HTTP Transport.
|
||||
func MakeInsecureTransport(config *KubeletClientConfig) (http.RoundTripper, error) {
|
||||
return makeTransport(config, true)
|
||||
}
|
||||
|
||||
// makeTransport creates a RoundTripper for HTTP Transport.
|
||||
func makeTransport(config *KubeletClientConfig, insecureSkipTLSVerify bool) (http.RoundTripper, error) {
|
||||
// do the insecureSkipTLSVerify on the pre-transport *before* we go get a potentially cached connection.
|
||||
// transportConfig always produces a new struct pointer.
|
||||
preTLSConfig := config.transportConfig()
|
||||
if insecureSkipTLSVerify && preTLSConfig != nil {
|
||||
preTLSConfig.TLS.Insecure = true
|
||||
preTLSConfig.TLS.CAData = nil
|
||||
preTLSConfig.TLS.CAFile = ""
|
||||
}
|
||||
|
||||
tlsConfig, err := transport.TLSConfigFor(preTLSConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -148,6 +168,8 @@ type NodeConnectionInfoGetter struct {
|
||||
defaultPort int
|
||||
// transport is the transport to use to send a request to all kubelets
|
||||
transport http.RoundTripper
|
||||
// insecureSkipTLSVerifyTransport is the transport to use if the kube-apiserver wants to skip verifying the TLS certificate of the kubelet
|
||||
insecureSkipTLSVerifyTransport http.RoundTripper
|
||||
// preferredAddressTypes specifies the preferred order to use to find a node address
|
||||
preferredAddressTypes []v1.NodeAddressType
|
||||
}
|
||||
@ -163,6 +185,10 @@ func NewNodeConnectionInfoGetter(nodes NodeGetter, config KubeletClientConfig) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
insecureSkipTLSVerifyTransport, err := MakeInsecureTransport(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
types := []v1.NodeAddressType{}
|
||||
for _, t := range config.PreferredAddressTypes {
|
||||
@ -170,10 +196,11 @@ func NewNodeConnectionInfoGetter(nodes NodeGetter, config KubeletClientConfig) (
|
||||
}
|
||||
|
||||
return &NodeConnectionInfoGetter{
|
||||
nodes: nodes,
|
||||
scheme: scheme,
|
||||
defaultPort: int(config.Port),
|
||||
transport: transport,
|
||||
nodes: nodes,
|
||||
scheme: scheme,
|
||||
defaultPort: int(config.Port),
|
||||
transport: transport,
|
||||
insecureSkipTLSVerifyTransport: insecureSkipTLSVerifyTransport,
|
||||
|
||||
preferredAddressTypes: types,
|
||||
}, nil
|
||||
@ -199,9 +226,10 @@ func (k *NodeConnectionInfoGetter) GetConnectionInfo(ctx context.Context, nodeNa
|
||||
}
|
||||
|
||||
return &ConnectionInfo{
|
||||
Scheme: k.scheme,
|
||||
Hostname: host,
|
||||
Port: strconv.Itoa(port),
|
||||
Transport: k.transport,
|
||||
Scheme: k.scheme,
|
||||
Hostname: host,
|
||||
Port: strconv.Itoa(port),
|
||||
Transport: k.transport,
|
||||
InsecureSkipTLSVerifyTransport: k.insecureSkipTLSVerifyTransport,
|
||||
}, nil
|
||||
}
|
||||
|
@ -17,6 +17,12 @@ limitations under the License.
|
||||
package client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
restclient "k8s.io/client-go/rest"
|
||||
@ -63,3 +69,59 @@ func TestMakeTransportValid(t *testing.T) {
|
||||
t.Error("rt should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeInsecureTransport(t *testing.T) {
|
||||
testServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
||||
testURL, err := url.Parse(testServer.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, portStr, err := net.SplitHostPort(testURL.Host)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
port, err := strconv.ParseUint(portStr, 10, 32)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config := &KubeletClientConfig{
|
||||
Port: uint(port),
|
||||
EnableHTTPS: true,
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CertFile: "../../client/testdata/mycertvalid.cer",
|
||||
// TLS Configuration, only applies if EnableHTTPS is true.
|
||||
KeyFile: "../../client/testdata/mycertvalid.key",
|
||||
// TLS Configuration, only applies if EnableHTTPS is true.
|
||||
CAFile: "../../client/testdata/myCA.cer",
|
||||
},
|
||||
}
|
||||
|
||||
rt, err := MakeInsecureTransport(config)
|
||||
if err != nil {
|
||||
t.Errorf("Not expecting an error #%v", err)
|
||||
}
|
||||
if rt == nil {
|
||||
t.Error("rt should not be nil")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, testServer.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response, err := rt.RoundTrip(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if response.StatusCode != http.StatusOK {
|
||||
dump, err := httputil.DumpResponse(response, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Fatal(string(dump))
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ go_library(
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/helper/qos:go_default_library",
|
||||
"//pkg/apis/core/validation:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubelet/client:go_default_library",
|
||||
"//pkg/proxy/util:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
@ -32,6 +33,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -17,6 +17,7 @@ go_library(
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/validation:go_default_library",
|
||||
"//pkg/capabilities:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubelet/client:go_default_library",
|
||||
"//pkg/registry/core/pod:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
|
@ -25,8 +25,10 @@ import (
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
genericrest "k8s.io/apiserver/pkg/registry/generic/rest"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/registry/core/pod"
|
||||
)
|
||||
@ -67,6 +69,10 @@ func (r *LogREST) Get(ctx context.Context, name string, opts runtime.Object) (ru
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid options object: %#v", opts)
|
||||
}
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.AllowInsecureBackendProxy) {
|
||||
logOpts.InsecureSkipTLSVerifyBackend = false
|
||||
}
|
||||
|
||||
if errs := validation.ValidatePodLogOptions(logOpts); len(errs) > 0 {
|
||||
return nil, errors.NewInvalid(api.Kind("PodLogOptions"), name, errs)
|
||||
}
|
||||
|
@ -37,11 +37,13 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
"k8s.io/apiserver/pkg/storage/names"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper/qos"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
||||
)
|
||||
@ -382,6 +384,10 @@ func LogLocation(
|
||||
Path: fmt.Sprintf("/containerLogs/%s/%s/%s", pod.Namespace, pod.Name, container),
|
||||
RawQuery: params.Encode(),
|
||||
}
|
||||
|
||||
if opts.InsecureSkipTLSVerifyBackend && utilfeature.DefaultFeatureGate.Enabled(features.AllowInsecureBackendProxy) {
|
||||
return loc, nodeInfo.InsecureSkipTLSVerifyTransport, nil
|
||||
}
|
||||
return loc, nodeInfo.Transport, nil
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ package pod
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
@ -320,31 +321,56 @@ func (g mockPodGetter) Get(context.Context, string, *metav1.GetOptions) (runtime
|
||||
func TestCheckLogLocation(t *testing.T) {
|
||||
ctx := genericapirequest.NewDefaultContext()
|
||||
tcs := []struct {
|
||||
in *api.Pod
|
||||
opts *api.PodLogOptions
|
||||
expectedErr error
|
||||
name string
|
||||
in *api.Pod
|
||||
opts *api.PodLogOptions
|
||||
expectedErr error
|
||||
expectedTransport http.RoundTripper
|
||||
}{
|
||||
{
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: errors.NewBadRequest("a container name must be specified for pod test"),
|
||||
},
|
||||
{
|
||||
name: "simple",
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{Name: "mycontainer"},
|
||||
},
|
||||
NodeName: "foo",
|
||||
},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: nil,
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: nil,
|
||||
expectedTransport: fakeSecureRoundTripper,
|
||||
},
|
||||
{
|
||||
name: "insecure",
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{Name: "mycontainer"},
|
||||
},
|
||||
NodeName: "foo",
|
||||
},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
opts: &api.PodLogOptions{
|
||||
InsecureSkipTLSVerifyBackend: true,
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectedTransport: fakeInsecureRoundTripper,
|
||||
},
|
||||
{
|
||||
name: "missing container",
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: errors.NewBadRequest("a container name must be specified for pod test"),
|
||||
expectedTransport: nil,
|
||||
},
|
||||
{
|
||||
name: "choice of two containers",
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
@ -354,10 +380,12 @@ func TestCheckLogLocation(t *testing.T) {
|
||||
},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: errors.NewBadRequest("a container name must be specified for pod test, choose one of: [container1 container2]"),
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: errors.NewBadRequest("a container name must be specified for pod test, choose one of: [container1 container2]"),
|
||||
expectedTransport: nil,
|
||||
},
|
||||
{
|
||||
name: "initcontainers",
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
@ -370,10 +398,12 @@ func TestCheckLogLocation(t *testing.T) {
|
||||
},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: errors.NewBadRequest("a container name must be specified for pod test, choose one of: [container1 container2] or one of the init containers: [initcontainer1]"),
|
||||
opts: &api.PodLogOptions{},
|
||||
expectedErr: errors.NewBadRequest("a container name must be specified for pod test, choose one of: [container1 container2] or one of the init containers: [initcontainer1]"),
|
||||
expectedTransport: nil,
|
||||
},
|
||||
{
|
||||
name: "bad container",
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
@ -386,30 +416,44 @@ func TestCheckLogLocation(t *testing.T) {
|
||||
opts: &api.PodLogOptions{
|
||||
Container: "unknown",
|
||||
},
|
||||
expectedErr: errors.NewBadRequest("container unknown is not valid for pod test"),
|
||||
expectedErr: errors.NewBadRequest("container unknown is not valid for pod test"),
|
||||
expectedTransport: nil,
|
||||
},
|
||||
{
|
||||
name: "good with two containers",
|
||||
in: &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{Name: "container1"},
|
||||
{Name: "container2"},
|
||||
},
|
||||
NodeName: "foo",
|
||||
},
|
||||
Status: api.PodStatus{},
|
||||
},
|
||||
opts: &api.PodLogOptions{
|
||||
Container: "container2",
|
||||
},
|
||||
expectedErr: nil,
|
||||
expectedErr: nil,
|
||||
expectedTransport: fakeSecureRoundTripper,
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
getter := &mockPodGetter{tc.in}
|
||||
_, _, err := LogLocation(getter, nil, ctx, "test", tc.opts)
|
||||
if !reflect.DeepEqual(err, tc.expectedErr) {
|
||||
t.Errorf("expected %v, got %v", tc.expectedErr, err)
|
||||
}
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
getter := &mockPodGetter{tc.in}
|
||||
connectionGetter := &mockConnectionInfoGetter{&client.ConnectionInfo{
|
||||
Transport: fakeSecureRoundTripper,
|
||||
InsecureSkipTLSVerifyTransport: fakeInsecureRoundTripper,
|
||||
}}
|
||||
|
||||
_, actualTransport, err := LogLocation(getter, connectionGetter, ctx, "test", tc.opts)
|
||||
if !reflect.DeepEqual(err, tc.expectedErr) {
|
||||
t.Errorf("expected %v, got %v", tc.expectedErr, err)
|
||||
}
|
||||
if actualTransport != tc.expectedTransport {
|
||||
t.Errorf("expected %v, got %v", tc.expectedTransport, actualTransport)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -564,3 +608,16 @@ func TestGetPodIP(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeTransport struct {
|
||||
val string
|
||||
}
|
||||
|
||||
func (f fakeTransport) RoundTrip(*http.Request) (*http.Response, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
fakeSecureRoundTripper = fakeTransport{val: "secure"}
|
||||
fakeInsecureRoundTripper = fakeTransport{val: "insecure"}
|
||||
)
|
||||
|
@ -4808,6 +4808,15 @@ type PodLogOptions struct {
|
||||
// slightly more or slightly less than the specified limit.
|
||||
// +optional
|
||||
LimitBytes *int64 `json:"limitBytes,omitempty" protobuf:"varint,8,opt,name=limitBytes"`
|
||||
|
||||
// insecureSkipTLSVerifyBackend indicates that the apiserver should not confirm the validity of the
|
||||
// serving certificate of the backend it is connecting to. This will make the HTTPS connection between the apiserver
|
||||
// and the backend insecure. This means the apiserver cannot verify the log data it is receiving came from the real
|
||||
// kubelet. If the kubelet is configured to verify the apiserver's TLS credentials, it does not mean the
|
||||
// connection to the real kubelet is vulnerable to a man in the middle attack (e.g. an attacker could not intercept
|
||||
// the actual log data coming from the real kubelet).
|
||||
// +optional
|
||||
InsecureSkipTLSVerifyBackend bool `json:"insecureSkipTLSVerifyBackend,omitempty" protobuf:"varint,9,opt,name=insecureSkipTLSVerifyBackend"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
@ -89,6 +89,7 @@ filegroup(
|
||||
":package-srcs",
|
||||
"//test/integration/apiserver/admissionwebhook:all-srcs",
|
||||
"//test/integration/apiserver/apply:all-srcs",
|
||||
"//test/integration/apiserver/podlogs:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
30
test/integration/apiserver/podlogs/BUILD
Normal file
30
test/integration/apiserver/podlogs/BUILD
Normal file
@ -0,0 +1,30 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"podlogs_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app/options:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
27
test/integration/apiserver/podlogs/main_test.go
Normal file
27
test/integration/apiserver/podlogs/main_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package podlogs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
165
test/integration/apiserver/podlogs/podlogs_test.go
Normal file
165
test/integration/apiserver/podlogs/podlogs_test.go
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package podlogs
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestInsecurePodLogs(t *testing.T) {
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
clientSet, _ := framework.StartTestServer(t, stopCh, framework.TestServerSetup{
|
||||
ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
|
||||
opts.GenericServerRunOptions.MaxRequestBodyBytes = 1024 * 1024
|
||||
// I have no idea what this cert is, but it doesn't matter, we just want something that always fails validation
|
||||
opts.KubeletConfig.CAData = []byte(` -----BEGIN CERTIFICATE-----
|
||||
MIIDMDCCAhigAwIBAgIIHNPD7sig7YIwDQYJKoZIhvcNAQELBQAwNjESMBAGA1UE
|
||||
CxMJb3BlbnNoaWZ0MSAwHgYDVQQDExdhZG1pbi1rdWJlY29uZmlnLXNpZ25lcjAe
|
||||
Fw0xOTA1MzAxNTA3MzlaFw0yOTA1MjcxNTA3MzlaMDYxEjAQBgNVBAsTCW9wZW5z
|
||||
aGlmdDEgMB4GA1UEAxMXYWRtaW4ta3ViZWNvbmZpZy1zaWduZXIwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0dHk23lHRcuq06FzYDOl9J9+s8pnGxqA3
|
||||
IPcARI6ag/98aYe3ENwAB5e1i7AU2F2WiDZgj444w374XLdVgIK8zgQEm9yoqrlc
|
||||
+/ayO7ceKklrKHOMwh63LvGLEOqzhol2nFmBhXAZt+HyIoZHXN0IqlA92196+Dml
|
||||
0WOn1F4ce6JbAtEceFHPgLeI7KFmVaPz2796pBXh23ii6r7WvV1Rn9MKlMSBJQR4
|
||||
0LZzu9/j+GdnFXewdLAAMfgPzwEqv6h3PzvtUCjgdraHEm8Rs7s15S3PUmLK4RQS
|
||||
PsThx5BhJEGd/W6EzQ3BKoQfochhu3mnAQtW1J07CullySQ5Gg9fAgMBAAGjQjBA
|
||||
MA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQkTaaw
|
||||
YJSZ5k2Wd+OsM4GFMTGdqzANBgkqhkiG9w0BAQsFAAOCAQEAHK7+zBZPLqK+f9DT
|
||||
UEnpwRmZ0aeGS4YgbGIkqpjxJymVOwkRd5A1wslvVfGZ6yOQthF6KlCmqnPyJJMR
|
||||
I7FHw8j0h2ci90fEQ6IS90Y/ZJXkcgiK9Ncwa35GFGs8QrBxN4leGhtm84BnnBHN
|
||||
cTWpa4zcBwru0CRG7iHc66VX16X8jHB1iFeZ5W/FgY4MsE+G1Vze4mCXSPVI4BZ2
|
||||
/qlAgogjBivvSwQ9SFuCszg7IPjvT2ksm+Cf+8eT4YBqW41F85vBGR+FYK14yIla
|
||||
Bgqc+dJN9xS9Ah5gLiGQJ6C4niUA11piCpvMsy+j/LQ1Erx47KMar5fuMXYk7iPq
|
||||
1vqIwg==
|
||||
-----END CERTIFICATE-----
|
||||
`)
|
||||
},
|
||||
})
|
||||
|
||||
fakeKubeletServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write([]byte("fake-log"))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer fakeKubeletServer.Close()
|
||||
|
||||
fakeKubeletURL, err := url.Parse(fakeKubeletServer.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fakeKubeletHost, fakeKubeletPortStr, err := net.SplitHostPort(fakeKubeletURL.Host)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fakeKubeletPort, err := strconv.ParseUint(fakeKubeletPortStr, 10, 32)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
node, err := clientSet.CoreV1().Nodes().Create(&corev1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "fake"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
node.Status = corev1.NodeStatus{
|
||||
Addresses: []corev1.NodeAddress{
|
||||
{
|
||||
Type: corev1.NodeExternalIP,
|
||||
Address: fakeKubeletHost,
|
||||
},
|
||||
},
|
||||
DaemonEndpoints: corev1.NodeDaemonEndpoints{
|
||||
KubeletEndpoint: corev1.DaemonEndpoint{
|
||||
Port: int32(fakeKubeletPort),
|
||||
},
|
||||
},
|
||||
}
|
||||
node, err = clientSet.CoreV1().Nodes().UpdateStatus(node)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = clientSet.CoreV1().Namespaces().Create(&corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "ns"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = clientSet.CoreV1().ServiceAccounts("ns").Create(&corev1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "default", Namespace: "ns"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
falseRef := false
|
||||
pod, err := clientSet.CoreV1().Pods("ns").Create(&corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-pod", Namespace: "ns"},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "foo",
|
||||
Image: "some/image:latest",
|
||||
},
|
||||
},
|
||||
NodeName: node.Name,
|
||||
AutomountServiceAccountToken: &falseRef,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
insecureResult := clientSet.CoreV1().Pods("ns").GetLogs(pod.Name, &corev1.PodLogOptions{InsecureSkipTLSVerifyBackend: true}).Do()
|
||||
if err := insecureResult.Error(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
insecureStatusCode := 0
|
||||
insecureResult.StatusCode(&insecureStatusCode)
|
||||
if insecureStatusCode != http.StatusOK {
|
||||
t.Fatal(insecureStatusCode)
|
||||
}
|
||||
|
||||
secureResult := clientSet.CoreV1().Pods("ns").GetLogs(pod.Name, &corev1.PodLogOptions{}).Do()
|
||||
if err := secureResult.Error(); err == nil || !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
secureStatusCode := 0
|
||||
secureResult.StatusCode(&secureStatusCode)
|
||||
if secureStatusCode == http.StatusOK {
|
||||
raw, rawErr := secureResult.Raw()
|
||||
if rawErr != nil {
|
||||
t.Log(rawErr)
|
||||
}
|
||||
t.Log(string(raw))
|
||||
t.Fatal(secureStatusCode)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user