mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 05:27:21 +00:00
Merge pull request #63999 from mikedanese/validatetr
Automatic merge from submit-queue (batch tested with PRs 59938, 63777, 64577, 63999, 64431). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. svcacct: validate min and max expiration seconds on TokenRequest stop gap https://github.com/kubernetes/kubernetes/pull/63653 ```release-note TokenRequests now are required to have an expiration duration between 10 minutes and 2^32 seconds. ```
This commit is contained in:
commit
4993d01be1
@ -37,6 +37,7 @@ filegroup(
|
|||||||
"//pkg/apis/authentication/install:all-srcs",
|
"//pkg/apis/authentication/install:all-srcs",
|
||||||
"//pkg/apis/authentication/v1:all-srcs",
|
"//pkg/apis/authentication/v1:all-srcs",
|
||||||
"//pkg/apis/authentication/v1beta1:all-srcs",
|
"//pkg/apis/authentication/v1beta1:all-srcs",
|
||||||
|
"//pkg/apis/authentication/validation:all-srcs",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
26
pkg/apis/authentication/validation/BUILD
Normal file
26
pkg/apis/authentication/validation/BUILD
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["validation.go"],
|
||||||
|
importpath = "k8s.io/kubernetes/pkg/apis/authentication/validation",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/apis/authentication:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/validation/field: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"],
|
||||||
|
)
|
41
pkg/apis/authentication/validation/validation.go
Normal file
41
pkg/apis/authentication/validation/validation.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
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 validation contains methods to validate kinds in the
|
||||||
|
// authentication.k8s.io API group.
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/authentication"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateTokenRequest validates a TokenRequest.
|
||||||
|
func ValidateTokenRequest(tr *authentication.TokenRequest) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
specPath := field.NewPath("spec")
|
||||||
|
|
||||||
|
const min = 10 * time.Minute
|
||||||
|
if tr.Spec.ExpirationSeconds < int64(min.Seconds()) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(specPath.Child("expirationSeconds"), tr.Spec.ExpirationSeconds, "may not specify a duration less than 10 minutes"))
|
||||||
|
}
|
||||||
|
if tr.Spec.ExpirationSeconds > 1<<32 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(specPath.Child("expirationSeconds"), tr.Spec.ExpirationSeconds, "may not specify a duration larger than 2^32 seconds"))
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
@ -32,6 +32,7 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/pkg/registry/core/serviceaccount/storage",
|
importpath = "k8s.io/kubernetes/pkg/registry/core/serviceaccount/storage",
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/apis/authentication:go_default_library",
|
"//pkg/apis/authentication:go_default_library",
|
||||||
|
"//pkg/apis/authentication/validation:go_default_library",
|
||||||
"//pkg/apis/core:go_default_library",
|
"//pkg/apis/core:go_default_library",
|
||||||
"//pkg/printers:go_default_library",
|
"//pkg/printers:go_default_library",
|
||||||
"//pkg/printers/internalversion:go_default_library",
|
"//pkg/printers/internalversion:go_default_library",
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
|
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
|
||||||
|
authenticationvalidation "k8s.io/kubernetes/pkg/apis/authentication/validation"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
token "k8s.io/kubernetes/pkg/serviceaccount"
|
token "k8s.io/kubernetes/pkg/serviceaccount"
|
||||||
)
|
)
|
||||||
@ -48,6 +49,12 @@ type TokenREST struct {
|
|||||||
var _ = rest.NamedCreater(&TokenREST{})
|
var _ = rest.NamedCreater(&TokenREST{})
|
||||||
var _ = rest.GroupVersionKindProvider(&TokenREST{})
|
var _ = rest.GroupVersionKindProvider(&TokenREST{})
|
||||||
|
|
||||||
|
var gvk = schema.GroupVersionKind{
|
||||||
|
Group: authenticationapiv1.SchemeGroupVersion.Group,
|
||||||
|
Version: authenticationapiv1.SchemeGroupVersion.Version,
|
||||||
|
Kind: "TokenRequest",
|
||||||
|
}
|
||||||
|
|
||||||
func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
|
func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
|
||||||
if err := createValidation(obj); err != nil {
|
if err := createValidation(obj); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -55,6 +62,10 @@ func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object,
|
|||||||
|
|
||||||
out := obj.(*authenticationapi.TokenRequest)
|
out := obj.(*authenticationapi.TokenRequest)
|
||||||
|
|
||||||
|
if errs := authenticationvalidation.ValidateTokenRequest(out); len(errs) != 0 {
|
||||||
|
return nil, errors.NewInvalid(gvk.GroupKind(), "", errs)
|
||||||
|
}
|
||||||
|
|
||||||
svcacctObj, err := r.svcaccts.Get(ctx, name, &metav1.GetOptions{})
|
svcacctObj, err := r.svcaccts.Get(ctx, name, &metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -113,12 +124,8 @@ func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object,
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *TokenREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
func (r *TokenREST) GroupVersionKind(schema.GroupVersion) schema.GroupVersionKind {
|
||||||
return schema.GroupVersionKind{
|
return gvk
|
||||||
Group: authenticationapiv1.SchemeGroupVersion.Group,
|
|
||||||
Version: authenticationapiv1.SchemeGroupVersion.Version,
|
|
||||||
Kind: "TokenRequest",
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type getter interface {
|
type getter interface {
|
||||||
|
@ -51,6 +51,7 @@ go_test(
|
|||||||
"//test/integration:go_default_library",
|
"//test/integration:go_default_library",
|
||||||
"//test/integration/framework:go_default_library",
|
"//test/integration/framework:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
|
"//vendor/gopkg.in/square/go-jose.v2/jwt:go_default_library",
|
||||||
"//vendor/k8s.io/api/authentication/v1:go_default_library",
|
"//vendor/k8s.io/api/authentication/v1:go_default_library",
|
||||||
"//vendor/k8s.io/api/authentication/v1beta1:go_default_library",
|
"//vendor/k8s.io/api/authentication/v1beta1:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||||
|
@ -24,17 +24,20 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/square/go-jose.v2/jwt"
|
||||||
authenticationv1 "k8s.io/api/authentication/v1"
|
authenticationv1 "k8s.io/api/authentication/v1"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||||
|
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
externalclientset "k8s.io/client-go/kubernetes"
|
externalclientset "k8s.io/client-go/kubernetes"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/core"
|
||||||
serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||||
@ -118,7 +121,6 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
one = int64(1)
|
|
||||||
wrongUID = types.UID("wrong")
|
wrongUID = types.UID("wrong")
|
||||||
noUID = types.UID("")
|
noUID = types.UID("")
|
||||||
)
|
)
|
||||||
@ -126,8 +128,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
t.Run("bound to service account", func(t *testing.T) {
|
t.Run("bound to service account", func(t *testing.T) {
|
||||||
treq := &authenticationv1.TokenRequest{
|
treq := &authenticationv1.TokenRequest{
|
||||||
Spec: authenticationv1.TokenRequestSpec{
|
Spec: authenticationv1.TokenRequestSpec{
|
||||||
Audiences: []string{"api"},
|
Audiences: []string{"api"},
|
||||||
ExpirationSeconds: &one,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,8 +158,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
t.Run("bound to service account and pod", func(t *testing.T) {
|
t.Run("bound to service account and pod", func(t *testing.T) {
|
||||||
treq := &authenticationv1.TokenRequest{
|
treq := &authenticationv1.TokenRequest{
|
||||||
Spec: authenticationv1.TokenRequestSpec{
|
Spec: authenticationv1.TokenRequestSpec{
|
||||||
Audiences: []string{"api"},
|
Audiences: []string{"api"},
|
||||||
ExpirationSeconds: &one,
|
|
||||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
APIVersion: "v1",
|
APIVersion: "v1",
|
||||||
@ -211,8 +211,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
t.Run("bound to service account and secret", func(t *testing.T) {
|
t.Run("bound to service account and secret", func(t *testing.T) {
|
||||||
treq := &authenticationv1.TokenRequest{
|
treq := &authenticationv1.TokenRequest{
|
||||||
Spec: authenticationv1.TokenRequestSpec{
|
Spec: authenticationv1.TokenRequestSpec{
|
||||||
Audiences: []string{"api"},
|
Audiences: []string{"api"},
|
||||||
ExpirationSeconds: &one,
|
|
||||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||||
Kind: "Secret",
|
Kind: "Secret",
|
||||||
APIVersion: "v1",
|
APIVersion: "v1",
|
||||||
@ -266,8 +265,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
t.Run("bound to service account and pod running as different service account", func(t *testing.T) {
|
t.Run("bound to service account and pod running as different service account", func(t *testing.T) {
|
||||||
treq := &authenticationv1.TokenRequest{
|
treq := &authenticationv1.TokenRequest{
|
||||||
Spec: authenticationv1.TokenRequestSpec{
|
Spec: authenticationv1.TokenRequestSpec{
|
||||||
Audiences: []string{"api"},
|
Audiences: []string{"api"},
|
||||||
ExpirationSeconds: &one,
|
|
||||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
APIVersion: "v1",
|
APIVersion: "v1",
|
||||||
@ -289,8 +287,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
t.Run("expired token", func(t *testing.T) {
|
t.Run("expired token", func(t *testing.T) {
|
||||||
treq := &authenticationv1.TokenRequest{
|
treq := &authenticationv1.TokenRequest{
|
||||||
Spec: authenticationv1.TokenRequestSpec{
|
Spec: authenticationv1.TokenRequestSpec{
|
||||||
Audiences: []string{"api"},
|
Audiences: []string{"api"},
|
||||||
ExpirationSeconds: &one,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +300,26 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
doTokenReview(t, cs, treq, false)
|
doTokenReview(t, cs, treq, false)
|
||||||
time.Sleep(63 * time.Second)
|
|
||||||
|
// backdate the token
|
||||||
|
then := time.Now().Add(-2 * time.Hour)
|
||||||
|
sc := &jwt.Claims{
|
||||||
|
Subject: apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name),
|
||||||
|
Audience: jwt.Audience([]string{"api"}),
|
||||||
|
IssuedAt: jwt.NewNumericDate(then),
|
||||||
|
NotBefore: jwt.NewNumericDate(then),
|
||||||
|
Expiry: jwt.NewNumericDate(then.Add(time.Duration(60*60) * time.Second)),
|
||||||
|
}
|
||||||
|
coresa := core.ServiceAccount{
|
||||||
|
ObjectMeta: sa.ObjectMeta,
|
||||||
|
}
|
||||||
|
_, pc := serviceaccount.Claims(coresa, nil, nil, 0, nil)
|
||||||
|
tok, err := masterConfig.ExtraConfig.ServiceAccountIssuer.GenerateToken(sc, pc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err signing expired token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
treq.Status.Token = tok
|
||||||
doTokenReview(t, cs, treq, true)
|
doTokenReview(t, cs, treq, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -346,8 +362,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
t.Run("a token should be invalid after recreating same name pod", func(t *testing.T) {
|
t.Run("a token should be invalid after recreating same name pod", func(t *testing.T) {
|
||||||
treq := &authenticationv1.TokenRequest{
|
treq := &authenticationv1.TokenRequest{
|
||||||
Spec: authenticationv1.TokenRequestSpec{
|
Spec: authenticationv1.TokenRequestSpec{
|
||||||
Audiences: []string{"api"},
|
Audiences: []string{"api"},
|
||||||
ExpirationSeconds: &one,
|
|
||||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
APIVersion: "v1",
|
APIVersion: "v1",
|
||||||
@ -386,8 +401,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
|||||||
t.Run("a token should be invalid after recreating same name secret", func(t *testing.T) {
|
t.Run("a token should be invalid after recreating same name secret", func(t *testing.T) {
|
||||||
treq := &authenticationv1.TokenRequest{
|
treq := &authenticationv1.TokenRequest{
|
||||||
Spec: authenticationv1.TokenRequestSpec{
|
Spec: authenticationv1.TokenRequestSpec{
|
||||||
Audiences: []string{"api"},
|
Audiences: []string{"api"},
|
||||||
ExpirationSeconds: &one,
|
|
||||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||||
Kind: "Secret",
|
Kind: "Secret",
|
||||||
APIVersion: "v1",
|
APIVersion: "v1",
|
||||||
|
Loading…
Reference in New Issue
Block a user