rebase: update k8s.io packages to v0.29.0

Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
Niels de Vos
2023-12-20 13:23:59 +01:00
committed by mergify[bot]
parent 328a264202
commit f080b9e0c9
367 changed files with 21340 additions and 11878 deletions

View File

@ -0,0 +1,154 @@
/*
Copyright 2023 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 cel
import (
"fmt"
"github.com/google/cel-go/cel"
"k8s.io/apimachinery/pkg/util/version"
apiservercel "k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/environment"
)
const (
claimsVarName = "claims"
userVarName = "user"
)
// compiler implements the Compiler interface.
type compiler struct {
// varEnvs is a map of CEL environments, keyed by the name of the CEL variable.
// The CEL variable is available to the expression.
// We have 2 environments, one for claims and one for user.
varEnvs map[string]*environment.EnvSet
}
// NewCompiler returns a new Compiler.
func NewCompiler(env *environment.EnvSet) Compiler {
return &compiler{
varEnvs: mustBuildEnvs(env),
}
}
// CompileClaimsExpression compiles the given expressionAccessor into a CEL program that can be evaluated.
// The claims CEL variable is available to the expression.
func (c compiler) CompileClaimsExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error) {
return c.compile(expressionAccessor, claimsVarName)
}
// CompileUserExpression compiles the given expressionAccessor into a CEL program that can be evaluated.
// The user CEL variable is available to the expression.
func (c compiler) CompileUserExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error) {
return c.compile(expressionAccessor, userVarName)
}
func (c compiler) compile(expressionAccessor ExpressionAccessor, envVarName string) (CompilationResult, error) {
resultError := func(errorString string, errType apiservercel.ErrorType) (CompilationResult, error) {
return CompilationResult{}, &apiservercel.Error{
Type: errType,
Detail: errorString,
}
}
env, err := c.varEnvs[envVarName].Env(environment.StoredExpressions)
if err != nil {
return resultError(fmt.Sprintf("unexpected error loading CEL environment: %v", err), apiservercel.ErrorTypeInternal)
}
ast, issues := env.Compile(expressionAccessor.GetExpression())
if issues != nil {
return resultError("compilation failed: "+issues.String(), apiservercel.ErrorTypeInvalid)
}
found := false
returnTypes := expressionAccessor.ReturnTypes()
for _, returnType := range returnTypes {
if ast.OutputType() == returnType || cel.AnyType == returnType {
found = true
break
}
}
if !found {
var reason string
if len(returnTypes) == 1 {
reason = fmt.Sprintf("must evaluate to %v", returnTypes[0].String())
} else {
reason = fmt.Sprintf("must evaluate to one of %v", returnTypes)
}
return resultError(reason, apiservercel.ErrorTypeInvalid)
}
if _, err = cel.AstToCheckedExpr(ast); err != nil {
// should be impossible since env.Compile returned no issues
return resultError("unexpected compilation error: "+err.Error(), apiservercel.ErrorTypeInternal)
}
prog, err := env.Program(ast)
if err != nil {
return resultError("program instantiation failed: "+err.Error(), apiservercel.ErrorTypeInternal)
}
return CompilationResult{
Program: prog,
ExpressionAccessor: expressionAccessor,
}, nil
}
func buildUserType() *apiservercel.DeclType {
field := func(name string, declType *apiservercel.DeclType, required bool) *apiservercel.DeclField {
return apiservercel.NewDeclField(name, declType, required, nil, nil)
}
fields := func(fields ...*apiservercel.DeclField) map[string]*apiservercel.DeclField {
result := make(map[string]*apiservercel.DeclField, len(fields))
for _, f := range fields {
result[f.Name] = f
}
return result
}
return apiservercel.NewObjectType("kubernetes.UserInfo", fields(
field("username", apiservercel.StringType, false),
field("uid", apiservercel.StringType, false),
field("groups", apiservercel.NewListType(apiservercel.StringType, -1), false),
field("extra", apiservercel.NewMapType(apiservercel.StringType, apiservercel.NewListType(apiservercel.StringType, -1), -1), false),
))
}
func mustBuildEnvs(baseEnv *environment.EnvSet) map[string]*environment.EnvSet {
buildEnvSet := func(envOpts []cel.EnvOption, declTypes []*apiservercel.DeclType) *environment.EnvSet {
env, err := baseEnv.Extend(environment.VersionedOptions{
IntroducedVersion: version.MajorMinor(1, 0),
EnvOptions: envOpts,
DeclTypes: declTypes,
})
if err != nil {
panic(fmt.Sprintf("environment misconfigured: %v", err))
}
return env
}
userType := buildUserType()
claimsType := apiservercel.NewMapType(apiservercel.StringType, apiservercel.AnyType, -1)
envs := make(map[string]*environment.EnvSet, 2) // build two environments, one for claims and one for user
envs[claimsVarName] = buildEnvSet([]cel.EnvOption{cel.Variable(claimsVarName, claimsType.CelType())}, []*apiservercel.DeclType{claimsType})
envs[userVarName] = buildEnvSet([]cel.EnvOption{cel.Variable(userVarName, userType.CelType())}, []*apiservercel.DeclType{userType})
return envs
}

View File

@ -0,0 +1,147 @@
/*
Copyright 2023 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 cel contains the CEL related interfaces and structs for authentication.
package cel
import (
"context"
celgo "github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types/ref"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// ExpressionAccessor is an interface that provides access to a CEL expression.
type ExpressionAccessor interface {
GetExpression() string
ReturnTypes() []*celgo.Type
}
// CompilationResult represents a compiled validations expression.
type CompilationResult struct {
Program celgo.Program
ExpressionAccessor ExpressionAccessor
}
// EvaluationResult contains the minimal required fields and metadata of a cel evaluation
type EvaluationResult struct {
EvalResult ref.Val
ExpressionAccessor ExpressionAccessor
}
// Compiler provides a CEL expression compiler configured with the desired authentication related CEL variables.
type Compiler interface {
CompileClaimsExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error)
CompileUserExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error)
}
// ClaimsMapper provides a CEL expression mapper configured with the claims CEL variable.
type ClaimsMapper interface {
// EvalClaimMapping evaluates the given claim mapping expression and returns a EvaluationResult.
// This is used for username, groups and uid claim mapping that contains a single expression.
EvalClaimMapping(ctx context.Context, claims *unstructured.Unstructured) (EvaluationResult, error)
// EvalClaimMappings evaluates the given expressions and returns a list of EvaluationResult.
// This is used for extra claim mapping and claim validation that contains a list of expressions.
EvalClaimMappings(ctx context.Context, claims *unstructured.Unstructured) ([]EvaluationResult, error)
}
// UserMapper provides a CEL expression mapper configured with the user CEL variable.
type UserMapper interface {
// EvalUser evaluates the given user expressions and returns a list of EvaluationResult.
// This is used for user validation that contains a list of expressions.
EvalUser(ctx context.Context, userInfo *unstructured.Unstructured) ([]EvaluationResult, error)
}
var _ ExpressionAccessor = &ClaimMappingExpression{}
// ClaimMappingExpression is a CEL expression that maps a claim.
type ClaimMappingExpression struct {
Expression string
}
// GetExpression returns the CEL expression.
func (v *ClaimMappingExpression) GetExpression() string {
return v.Expression
}
// ReturnTypes returns the CEL expression return types.
func (v *ClaimMappingExpression) ReturnTypes() []*celgo.Type {
// return types is only used for validation. The claims variable that's available
// to the claim mapping expressions is a map[string]interface{}, so we can't
// really know what the return type is during compilation. Strict type checking
// is done during evaluation.
return []*celgo.Type{celgo.AnyType}
}
var _ ExpressionAccessor = &ClaimValidationCondition{}
// ClaimValidationCondition is a CEL expression that validates a claim.
type ClaimValidationCondition struct {
Expression string
Message string
}
// GetExpression returns the CEL expression.
func (v *ClaimValidationCondition) GetExpression() string {
return v.Expression
}
// ReturnTypes returns the CEL expression return types.
func (v *ClaimValidationCondition) ReturnTypes() []*celgo.Type {
return []*celgo.Type{celgo.BoolType}
}
var _ ExpressionAccessor = &ExtraMappingExpression{}
// ExtraMappingExpression is a CEL expression that maps an extra to a list of values.
type ExtraMappingExpression struct {
Key string
Expression string
}
// GetExpression returns the CEL expression.
func (v *ExtraMappingExpression) GetExpression() string {
return v.Expression
}
// ReturnTypes returns the CEL expression return types.
func (v *ExtraMappingExpression) ReturnTypes() []*celgo.Type {
// return types is only used for validation. The claims variable that's available
// to the claim mapping expressions is a map[string]interface{}, so we can't
// really know what the return type is during compilation. Strict type checking
// is done during evaluation.
return []*celgo.Type{celgo.AnyType}
}
var _ ExpressionAccessor = &UserValidationCondition{}
// UserValidationCondition is a CEL expression that validates a User.
type UserValidationCondition struct {
Expression string
Message string
}
// GetExpression returns the CEL expression.
func (v *UserValidationCondition) GetExpression() string {
return v.Expression
}
// ReturnTypes returns the CEL expression return types.
func (v *UserValidationCondition) ReturnTypes() []*celgo.Type {
return []*celgo.Type{celgo.BoolType}
}

View File

@ -0,0 +1,97 @@
/*
Copyright 2023 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 cel
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
var _ ClaimsMapper = &mapper{}
var _ UserMapper = &mapper{}
// mapper implements the ClaimsMapper and UserMapper interface.
type mapper struct {
compilationResults []CompilationResult
}
// CELMapper is a struct that holds the compiled expressions for
// username, groups, uid, extra, claimValidation and userValidation
type CELMapper struct {
Username ClaimsMapper
Groups ClaimsMapper
UID ClaimsMapper
Extra ClaimsMapper
ClaimValidationRules ClaimsMapper
UserValidationRules UserMapper
}
// NewClaimsMapper returns a new ClaimsMapper.
func NewClaimsMapper(compilationResults []CompilationResult) ClaimsMapper {
return &mapper{
compilationResults: compilationResults,
}
}
// NewUserMapper returns a new UserMapper.
func NewUserMapper(compilationResults []CompilationResult) UserMapper {
return &mapper{
compilationResults: compilationResults,
}
}
// EvalClaimMapping evaluates the given claim mapping expression and returns a EvaluationResult.
func (m *mapper) EvalClaimMapping(ctx context.Context, claims *unstructured.Unstructured) (EvaluationResult, error) {
results, err := m.eval(ctx, map[string]interface{}{claimsVarName: claims.Object})
if err != nil {
return EvaluationResult{}, err
}
if len(results) != 1 {
return EvaluationResult{}, fmt.Errorf("expected 1 evaluation result, got %d", len(results))
}
return results[0], nil
}
// EvalClaimMappings evaluates the given expressions and returns a list of EvaluationResult.
func (m *mapper) EvalClaimMappings(ctx context.Context, claims *unstructured.Unstructured) ([]EvaluationResult, error) {
return m.eval(ctx, map[string]interface{}{claimsVarName: claims.Object})
}
// EvalUser evaluates the given user expressions and returns a list of EvaluationResult.
func (m *mapper) EvalUser(ctx context.Context, userInfo *unstructured.Unstructured) ([]EvaluationResult, error) {
return m.eval(ctx, map[string]interface{}{userVarName: userInfo.Object})
}
func (m *mapper) eval(ctx context.Context, input map[string]interface{}) ([]EvaluationResult, error) {
evaluations := make([]EvaluationResult, len(m.compilationResults))
for i, compilationResult := range m.compilationResults {
var evaluation = &evaluations[i]
evaluation.ExpressionAccessor = compilationResult.ExpressionAccessor
evalResult, _, err := compilationResult.Program.ContextEval(ctx, input)
if err != nil {
return nil, fmt.Errorf("expression '%s' resulted in error: %w", compilationResult.ExpressionAccessor.GetExpression(), err)
}
evaluation.EvalResult = evalResult
}
return evaluations, nil
}

View File

@ -148,6 +148,33 @@ func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.R
}
}
/*
kubernetes mutual (2-way) x509 between client and apiserver:
1. apiserver sending its apiserver certificate along with its publickey to client
2. client verifies the apiserver certificate sent against its cluster certificate authority data
3. client sending its client certificate along with its public key to the apiserver
>4. apiserver verifies the client certificate sent against its cluster certificate authority data
description:
here, with this function,
client certificate and pub key sent during the handshake process
are verified by apiserver against its cluster certificate authority data
normal args related to this stage:
--client-ca-file string If set, any request presenting a client certificate signed by
one of the authorities in the client-ca-file is authenticated with an identity
corresponding to the CommonName of the client certificate.
(retrievable from "kube-apiserver --help" command)
(suggested by @deads2k)
see also:
- for the step 1, see: staging/src/k8s.io/apiserver/pkg/server/options/serving.go
- for the step 2, see: staging/src/k8s.io/client-go/transport/transport.go
- for the step 3, see: staging/src/k8s.io/client-go/transport/transport.go
*/
remaining := req.TLS.PeerCertificates[0].NotAfter.Sub(time.Now())
clientCertificateExpirationHistogram.WithContext(req.Context()).Observe(remaining.Seconds())
chains, err := req.TLS.PeerCertificates[0].Verify(optsCopy)

View File

@ -36,12 +36,21 @@ const (
ServiceAccountUsernameSeparator = ":"
ServiceAccountGroupPrefix = "system:serviceaccounts:"
AllServiceAccountsGroup = "system:serviceaccounts"
// CredentialIDKey is the key used in a user's "extra" to specify the unique
// identifier for this identity document).
CredentialIDKey = "authentication.kubernetes.io/credential-id"
// PodNameKey is the key used in a user's "extra" to specify the pod name of
// the authenticating request.
PodNameKey = "authentication.kubernetes.io/pod-name"
// PodUIDKey is the key used in a user's "extra" to specify the pod UID of
// the authenticating request.
PodUIDKey = "authentication.kubernetes.io/pod-uid"
// NodeNameKey is the key used in a user's "extra" to specify the node name of
// the authenticating request.
NodeNameKey = "authentication.kubernetes.io/node-name"
// NodeUIDKey is the key used in a user's "extra" to specify the node UID of
// the authenticating request.
NodeUIDKey = "authentication.kubernetes.io/node-uid"
)
// MakeUsername generates a username from the given namespace and ServiceAccount name.
@ -119,6 +128,8 @@ func UserInfo(namespace, name, uid string) user.Info {
type ServiceAccountInfo struct {
Name, Namespace, UID string
PodName, PodUID string
CredentialID string
NodeName, NodeUID string
}
func (sa *ServiceAccountInfo) UserInfo() user.Info {
@ -127,15 +138,43 @@ func (sa *ServiceAccountInfo) UserInfo() user.Info {
UID: sa.UID,
Groups: MakeGroupNames(sa.Namespace),
}
if sa.PodName != "" && sa.PodUID != "" {
info.Extra = map[string][]string{
PodNameKey: {sa.PodName},
PodUIDKey: {sa.PodUID},
if info.Extra == nil {
info.Extra = make(map[string][]string)
}
info.Extra[PodNameKey] = []string{sa.PodName}
info.Extra[PodUIDKey] = []string{sa.PodUID}
}
if sa.CredentialID != "" {
if info.Extra == nil {
info.Extra = make(map[string][]string)
}
info.Extra[CredentialIDKey] = []string{sa.CredentialID}
}
if sa.NodeName != "" {
if info.Extra == nil {
info.Extra = make(map[string][]string)
}
info.Extra[NodeNameKey] = []string{sa.NodeName}
// node UID is optional and will only be set if the node name is set
if sa.NodeUID != "" {
info.Extra[NodeUIDKey] = []string{sa.NodeUID}
}
}
return info
}
// CredentialIDForJTI converts a given JTI string into a credential identifier for use in a
// users 'extra' info.
func CredentialIDForJTI(jti string) string {
if len(jti) == 0 {
return ""
}
return "JTI=" + jti
}
// IsServiceAccountToken returns true if the secret is a valid api token for the service account
func IsServiceAccountToken(secret *v1.Secret, sa *v1.ServiceAccount) bool {
if secret.Type != v1.SecretTypeServiceAccountToken {