Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow kube-scheduler to tolerate cluster auth config lookup failure #71755

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/kube-scheduler/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func NewOptions() (*Options, error) {
},
}

o.Authentication.TolerateInClusterLookupFailure = true
o.Authentication.RemoteKubeConfigFileOptional = true
o.Authorization.RemoteKubeConfigFileOptional = true
o.Authorization.AlwaysAllowPaths = []string{"/healthz"}
Expand Down
1 change: 1 addition & 0 deletions staging/src/k8s.io/apiserver/pkg/server/options/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ go_test(
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ type DelegatingAuthenticationOptions struct {
ClientCert ClientCertAuthenticationOptions
RequestHeader RequestHeaderAuthenticationOptions

// SkipInClusterLookup indicates missing authentication configuration should not be retrieved from the cluster configmap
SkipInClusterLookup bool

// TolerateInClusterLookupFailure indicates failures to look up authentication configuration from the cluster configmap should not be fatal.
// Setting this can result in an authenticator that will reject all requests.
TolerateInClusterLookupFailure bool
}

func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions {
Expand Down Expand Up @@ -160,6 +165,9 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
"configuration from the cluster.")
fs.BoolVar(&s.TolerateInClusterLookupFailure, "authentication-tolerate-lookup-failure", s.TolerateInClusterLookupFailure, ""+
"If true, failures to look up missing authentication configuration from the cluster are not considered fatal. "+
"Note that this can result in authentication that treats all requests as anonymous.")
}

func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo, servingInfo *server.SecureServingInfo, openAPIConfig *openapicommon.Config) error {
Expand Down Expand Up @@ -187,7 +195,13 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo,
if !s.SkipInClusterLookup {
err := s.lookupMissingConfigInCluster(client)
if err != nil {
return err
if s.TolerateInClusterLookupFailure {
klog.Warningf("Error looking up in-cluster authentication configuration: %v", err)
klog.Warningf("Continuing without authentication configuration. This may treat all requests as anonymous.")
klog.Warningf("To require authentication configuration lookup to succeed, set --authentication-tolerate-lookup-failure=false")
} else {
return err
}
}
}

Expand Down
133 changes: 133 additions & 0 deletions staging/src/k8s.io/apiserver/pkg/server/options/authentication_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ limitations under the License.
package options

import (
"io/ioutil"
"net/http"
"os"
"reflect"
"testing"

"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
"k8s.io/apiserver/pkg/server"
openapicommon "k8s.io/kube-openapi/pkg/common"
)

func TestToAuthenticationRequestHeaderConfig(t *testing.T) {
Expand Down Expand Up @@ -66,3 +71,131 @@ func TestToAuthenticationRequestHeaderConfig(t *testing.T) {
})
}
}

func TestApplyToFallback(t *testing.T) {

f, err := ioutil.TempFile("", "authkubeconfig")
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())

if err := ioutil.WriteFile(f.Name(), []byte(`
apiVersion: v1
kind: Config
clusters:
- cluster:
server: http://localhost:56789
name: cluster
contexts:
- context:
cluster: cluster
name: cluster
current-context: cluster
`), os.FileMode(0755)); err != nil {
t.Fatal(err)
}
remoteKubeconfig := f.Name()

testcases := []struct {
name string
options *DelegatingAuthenticationOptions
expectError bool
expectAuthenticator bool
expectTokenAnonymous bool
expectTokenErrors bool
}{
{
name: "empty",
options: nil,
expectError: false,
expectAuthenticator: false,
},
{
name: "default",
options: NewDelegatingAuthenticationOptions(),
expectError: true, // in-cluster client building fails, no kubeconfig provided
expectAuthenticator: false,
},
{
name: "optional kubeconfig",
options: func() *DelegatingAuthenticationOptions {
opts := NewDelegatingAuthenticationOptions()
opts.RemoteKubeConfigFileOptional = true
return opts
}(),
expectError: false, // in-cluster client building fails, no kubeconfig required
expectAuthenticator: true,
expectTokenAnonymous: true, // no token validator available
},
{
name: "valid client, failed cluster info lookup",
options: func() *DelegatingAuthenticationOptions {
opts := NewDelegatingAuthenticationOptions()
opts.RemoteKubeConfigFile = remoteKubeconfig
return opts
}(),
expectError: true, // client building is valid, remote config lookup fails
expectAuthenticator: false,
},
{
name: "valid client, skip cluster info lookup",
options: func() *DelegatingAuthenticationOptions {
opts := NewDelegatingAuthenticationOptions()
opts.RemoteKubeConfigFile = remoteKubeconfig
opts.SkipInClusterLookup = true
return opts
}(),
expectError: false, // client building is valid, skipped cluster lookup
expectAuthenticator: true,
expectTokenErrors: true, // client fails making tokenreview calls
},
{
name: "valid client, tolerate failed cluster info lookup",
options: func() *DelegatingAuthenticationOptions {
opts := NewDelegatingAuthenticationOptions()
opts.RemoteKubeConfigFile = remoteKubeconfig
opts.TolerateInClusterLookupFailure = true
return opts
}(),
expectError: false, // client is valid, skipped cluster lookup
expectAuthenticator: true, // anonymous auth
expectTokenErrors: true, // client fails making tokenreview calls
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
c := &server.AuthenticationInfo{}
servingInfo := &server.SecureServingInfo{}
openAPIConfig := &openapicommon.Config{}

err := tc.options.ApplyTo(c, servingInfo, openAPIConfig)
if (err != nil) != tc.expectError {
t.Errorf("expected error=%v, got %v", tc.expectError, err)
}
if (c.Authenticator != nil) != tc.expectAuthenticator {
t.Errorf("expected authenticator=%v, got %#v", tc.expectError, c.Authenticator)
}
if c.Authenticator != nil {
{
result, ok, err := c.Authenticator.AuthenticateRequest(&http.Request{})
if err != nil || !ok || result == nil || result.User.GetName() != "system:anonymous" {
t.Errorf("expected anonymous, got %#v, %#v, %#v", result, ok, err)
}
}
{
result, ok, err := c.Authenticator.AuthenticateRequest(&http.Request{Header: http.Header{"Authorization": []string{"Bearer foo"}}})
if tc.expectTokenAnonymous {
if err != nil || !ok || result == nil || result.User.GetName() != "system:anonymous" {
t.Errorf("expected anonymous, got %#v, %#v, %#v", result, ok, err)
}
}
if tc.expectTokenErrors != (err != nil) {
t.Errorf("expected error=%v, got %#v, %#v, %#v", tc.expectTokenErrors, result, ok, err)
}
}
}
})
}
}