mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +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/v1:all-srcs",
|
||||
"//pkg/apis/authentication/v1beta1:all-srcs",
|
||||
"//pkg/apis/authentication/validation:all-srcs",
|
||||
],
|
||||
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",
|
||||
deps = [
|
||||
"//pkg/apis/authentication:go_default_library",
|
||||
"//pkg/apis/authentication/validation:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/printers/internalversion:go_default_library",
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
|
||||
authenticationvalidation "k8s.io/kubernetes/pkg/apis/authentication/validation"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
token "k8s.io/kubernetes/pkg/serviceaccount"
|
||||
)
|
||||
@ -48,6 +49,12 @@ type TokenREST struct {
|
||||
var _ = rest.NamedCreater(&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) {
|
||||
if err := createValidation(obj); err != nil {
|
||||
return nil, err
|
||||
@ -55,6 +62,10 @@ func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object,
|
||||
|
||||
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{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -113,12 +124,8 @@ func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object,
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *TokenREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{
|
||||
Group: authenticationapiv1.SchemeGroupVersion.Group,
|
||||
Version: authenticationapiv1.SchemeGroupVersion.Version,
|
||||
Kind: "TokenRequest",
|
||||
}
|
||||
func (r *TokenREST) GroupVersionKind(schema.GroupVersion) schema.GroupVersionKind {
|
||||
return gvk
|
||||
}
|
||||
|
||||
type getter interface {
|
||||
|
@ -51,6 +51,7 @@ go_test(
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework: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/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
|
@ -24,17 +24,20 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
externalclientset "k8s.io/client-go/kubernetes"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||
@ -118,7 +121,6 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
one = int64(1)
|
||||
wrongUID = types.UID("wrong")
|
||||
noUID = types.UID("")
|
||||
)
|
||||
@ -126,8 +128,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
||||
t.Run("bound to service account", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
ExpirationSeconds: &one,
|
||||
Audiences: []string{"api"},
|
||||
},
|
||||
}
|
||||
|
||||
@ -157,8 +158,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
||||
t.Run("bound to service account and pod", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
ExpirationSeconds: &one,
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
@ -211,8 +211,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
||||
t.Run("bound to service account and secret", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
ExpirationSeconds: &one,
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Secret",
|
||||
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) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
ExpirationSeconds: &one,
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
@ -289,8 +287,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
||||
t.Run("expired token", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
ExpirationSeconds: &one,
|
||||
Audiences: []string{"api"},
|
||||
},
|
||||
}
|
||||
|
||||
@ -303,7 +300,26 @@ func TestServiceAccountTokenCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
@ -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) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
ExpirationSeconds: &one,
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
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) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
ExpirationSeconds: &one,
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
|
Loading…
Reference in New Issue
Block a user