Skip to content

Commit

Permalink
Validate LastUsedAt for Token and ClusterAuthToken (rancher#480)
Browse files Browse the repository at this point in the history
Ref: rancher/rancher#45732

Co-authored-by: Peter Matseykanets <peter.matseykanets@suse.com>
  • Loading branch information
andreas-kupries and pmatseykanets authored Oct 8, 2024
1 parent 602351a commit 2d732bc
Show file tree
Hide file tree
Showing 9 changed files with 554 additions and 26 deletions.
80 changes: 57 additions & 23 deletions docs.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
# core/v1
# cluster.cattle.io/v3

## Namespace
## ClusterAuthToken

### Validation Checks

#### Invalid Fields - Create

When a ClusterAuthToken is created, the following checks take place:

- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`).

#### Invalid Fields - Update

When a ClusterAuthToken is updated, the following checks take place:

- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`).

# core/v1

## Namespace

### Validation Checks

Expand All @@ -24,7 +42,7 @@ The following labels are considered relevant for PSA enforcement:
- pod-security.kubernetes.io/warn
- pod-security.kubernetes.io/warn-version

## Secret
## Secret

### Validation Checks

Expand All @@ -43,9 +61,9 @@ places a `field.cattle.io/creatorId` annotation with the name of the user as the
Checks if there are any RoleBindings owned by this secret which provide access to a role granting access to this secret.
If yes, the webhook redacts the role, so that it only grants a deletion permission.

# management.cattle.io/v3
# management.cattle.io/v3

## ClusterProxyConfig
## ClusterProxyConfig

### Validation Checks

Expand All @@ -54,7 +72,7 @@ If yes, the webhook redacts the role, so that it only grants a deletion permissi
When creating a clusterproxyconfig, we check to make sure that one does not already exist for the given cluster.
Only 1 clusterproxyconfig per downstream cluster is ever permitted.

## ClusterRoleTemplateBinding
## ClusterRoleTemplateBinding

### Validation Checks

Expand Down Expand Up @@ -94,7 +112,7 @@ Users can update the following fields if they have not been set, but after they

In addition, as in the create validation, both a user subject and a group subject cannot be specified.

## Feature
## Feature

### Validation Checks

Expand All @@ -103,7 +121,7 @@ In addition, as in the create validation, both a user subject and a group subjec
The desired value must not change on new spec unless it's equal to the `lockedValue` or `lockedValue` is nil.
Due to the security impact of the `external-rules` feature flag, only users with admin permissions (`*` verbs on `*` resources in `*` APIGroups in all namespaces) can enable or disable this feature flag.

## FleetWorkspace
## FleetWorkspace

### Validation Checks

Expand All @@ -118,7 +136,7 @@ When a `FleetWorkspace` is created, it will create the following resources:
2. `ClusterRole`. It will create the cluster role that has * permission only to the current workspace.
3. Two `RoleBindings` to bind the current user to fleet-admin roles and `FleetWorkspace` roles.

## GlobalRole
## GlobalRole

### Validation Checks

Expand Down Expand Up @@ -149,7 +167,7 @@ The `globalroles.builtin` field is immutable, and new builtIn GlobalRoles cannot
If `globalroles.builtin` is true then all fields are immutable except `metadata` and `newUserDefault`.
If `globalroles.builtin` is true then the GlobalRole can not be deleted.

## GlobalRoleBinding
## GlobalRoleBinding

### Validation Checks

Expand Down Expand Up @@ -182,7 +200,7 @@ All RoleTemplates which are referred to in the `inheritedClusterRoles` field mus

When a GlobalRoleBinding is created an owner reference is created on the binding referring to the backing GlobalRole defined by `globalRoleName`.

## NodeDriver
## NodeDriver

### Validation Checks

Expand All @@ -192,7 +210,7 @@ Note: checks only run if a node driver is being disabled or deleted

This admission webhook prevents the disabling or deletion of a NodeDriver if there are any Nodes that are under management by said driver. If there are _any_ nodes that use the driver the request will be denied.

## Project
## Project

### Validation Checks

Expand Down Expand Up @@ -221,7 +239,7 @@ Limits for any resource must not be less than requests.

Adds the authz.management.cattle.io/creator-role-bindings annotation.

## ProjectRoleTemplateBinding
## ProjectRoleTemplateBinding

### Validation Checks

Expand Down Expand Up @@ -268,7 +286,7 @@ changed:

In addition, as in the create validation, both a user subject and a group subject cannot be specified.

## RoleTemplate
## RoleTemplate

### Validation Checks

Expand Down Expand Up @@ -307,7 +325,7 @@ If `roletemplates.builtin` is true then all fields are immutable except:

RoleTemplate can not be deleted if they are referenced by other RoleTemplates via `roletemplates.roleTemplateNames` or by GlobalRoles via `globalRoles.inheritedClusterRoles`

## Setting
## Setting

### Validation Checks

Expand Down Expand Up @@ -335,7 +353,23 @@ When a Setting is updated, the following checks take place:
have a status condition `AgentTlsStrictCheck` set to `True`, unless the new setting has an overriding
annotation `cattle.io/force=true`.

## UserAttribute
## Token

### Validation Checks

#### Invalid Fields - Create

When a Token is created, the following checks take place:

- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`).

#### Invalid Fields - Update

When a Token is updated, the following checks take place:

- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`).

## UserAttribute

### Validation Checks

Expand All @@ -355,9 +389,9 @@ When a UserAttribute is updated, the following checks take place:
- If set, `disableAfter` must be zero or a positive duration (e.g. `240h`).
- If set, `deleteAfter` must be zero or a positive duration (e.g. `240h`).

# provisioning.cattle.io/v1
# provisioning.cattle.io/v1

## Cluster
## Cluster

### Validation Checks

Expand Down Expand Up @@ -410,33 +444,33 @@ perform no mutations. If the value is not present or not `"true"`, compare the v
for each `machinePool`, to its' previous value. If the values are not identical, revert the value for the
`dynamicSchemaSpec` for the specific `machinePool`, but do not reject the request.

# rbac.authorization.k8s.io/v1
# rbac.authorization.k8s.io/v1

## ClusterRole
## ClusterRole

### Validation Checks

#### Invalid Fields - Update
Users cannot update or remove the following label after it has been added:
- authz.management.cattle.io/gr-owner

## ClusterRoleBinding
## ClusterRoleBinding

### Validation Checks

#### Invalid Fields - Update
Users cannot update or remove the following label after it has been added:
- authz.management.cattle.io/grb-owner

## Role
## Role

### Validation Checks

#### Invalid Fields - Update
Users cannot update or remove the following label after it has been added:
- authz.management.cattle.io/gr-owner

## RoleBinding
## RoleBinding

### Validation Checks

Expand Down
9 changes: 7 additions & 2 deletions pkg/codegen/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,18 @@ func generateDocs(resourcesBaseDir, outputFilePath string) (err error) {
if err != nil {
return err
}

docFiles, err := getDocFiles(resourcesBaseDir)
if err != nil {
return fmt.Errorf("unable to create documentation: %w", err)
}

currentGroup := ""
for _, docFile := range docFiles {
newGroup := docFile.group
if newGroup != currentGroup {
// our group has changed, output a new group header
groupFormatString := "# %s/%s \n"
groupFormatString := "# %s/%s\n"
if currentGroup != "" {
groupFormatString = "\n" + groupFormatString
}
Expand All @@ -59,10 +61,11 @@ func generateDocs(resourcesBaseDir, outputFilePath string) (err error) {
currentGroup = newGroup
}

_, err = fmt.Fprintf(outputFile, "\n## %s \n\n", docFile.resource)
_, err = fmt.Fprintf(outputFile, "\n## %s\n\n", docFile.resource)
if err != nil {
return fmt.Errorf("unable to write resource header for %s: %w", docFile.resource, err)
}

scanner := bufio.NewScanner(bytes.NewReader(docFile.content))
for scanner.Scan() {
line := scanner.Bytes()
Expand All @@ -81,6 +84,7 @@ func generateDocs(resourcesBaseDir, outputFilePath string) (err error) {
return fmt.Errorf("got an error scanning content for %s/%s.%s: %w", docFile.group, docFile.version, docFile.resource, err)
}
}

return nil
}

Expand All @@ -91,6 +95,7 @@ func getDocFiles(baseDir string) ([]docFile, error) {
if err != nil {
return nil, fmt.Errorf("unable to list entries in directory %s: %w", baseDir, err)
}

var docFiles []docFile
for _, entry := range entries {
entryPath := filepath.Join(baseDir, entry.Name())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Validation Checks

### Invalid Fields - Create

When a ClusterAuthToken is created, the following checks take place:

- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`).

### Invalid Fields - Update

When a ClusterAuthToken is updated, the following checks take place:

- If set, `lastUsedAt` must be a valid date time according to RFC3339 (e.g. `2023-11-29T00:00:00Z`).
93 changes: 93 additions & 0 deletions pkg/resources/cluster.cattle.io/v3/clusterauthtoken/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package clusterauthtoken

import (
"encoding/json"
"fmt"
"time"

"github.com/rancher/webhook/pkg/admission"
admissionv1 "k8s.io/api/admission/v1"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/utils/trace"
)

var gvr = schema.GroupVersionResource{
Group: "cluster.cattle.io",
Version: "v3",
Resource: "clusterauthtokens",
}

// Validator validates clusterauthtokens.
type Validator struct {
admitter admitter
}

// NewValidator returns a new Validator instance.
func NewValidator() *Validator {
return &Validator{
admitter: admitter{},
}
}

// GVR returns the GroupVersionResource.
func (v *Validator) GVR() schema.GroupVersionResource {
return gvr
}

// Operations returns list of operations handled by the validator.
func (v *Validator) Operations() []admissionregistrationv1.OperationType {
return []admissionregistrationv1.OperationType{admissionregistrationv1.Update, admissionregistrationv1.Create}
}

// ValidatingWebhook returns the ValidatingWebhook.
func (v *Validator) ValidatingWebhook(clientConfig admissionregistrationv1.WebhookClientConfig) []admissionregistrationv1.ValidatingWebhook {
return []admissionregistrationv1.ValidatingWebhook{
*admission.NewDefaultValidatingWebhook(v, clientConfig, admissionregistrationv1.ClusterScope, v.Operations()),
}
}

// Admitters returns the admitter objects.
func (v *Validator) Admitters() []admission.Admitter {
return []admission.Admitter{&v.admitter}
}

type admitter struct{}

// Admit handles the webhook admission requests.
func (a *admitter) Admit(request *admission.Request) (*admissionv1.AdmissionResponse, error) {
listTrace := trace.New("clusterAuthTokenValidator Admit", trace.Field{Key: "user", Value: request.UserInfo.Username})
defer listTrace.LogIfLong(admission.SlowTraceDuration)

if request.Operation == admissionv1.Create || request.Operation == admissionv1.Update {
err := a.validateTokenFields(request)
if err != nil {
return admission.ResponseBadRequest(err.Error()), nil
}
}

return admission.ResponseAllowed(), nil
}

// PartialClusterAuthToken represents raw values of ClusterAuthToken fields.
type PartialClusterAuthToken struct {
LastUsedAt *string `json:"lastUsedAt"`
}

func (a *admitter) validateTokenFields(request *admission.Request) error {
var partial PartialClusterAuthToken

err := json.Unmarshal(request.Object.Raw, &partial)
if err != nil {
return fmt.Errorf("failed to get PartialClusterAuthToken from request: %w", err)
}

if partial.LastUsedAt != nil {
if _, err = time.Parse(time.RFC3339, *partial.LastUsedAt); err != nil {
return field.TypeInvalid(field.NewPath("lastUsedAt"), partial.LastUsedAt, err.Error())
}
}

return nil
}
Loading

0 comments on commit 2d732bc

Please sign in to comment.