Skip to content

Commit

Permalink
Adding docs for some resources
Browse files Browse the repository at this point in the history
  • Loading branch information
MbolotSuse committed Jun 7, 2023
1 parent 9c124a4 commit a6c9cc2
Show file tree
Hide file tree
Showing 24 changed files with 259 additions and 62 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ It handles TLS certificates and the management of associated Secrets for secure

## Docs

Documentation on each of the CRDs that are validated can be found in `docs.md`. It is recommended to review the [kubernetes docs on CRDs](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) as well.
Documentation on each of the resources that are validated or mutated can be found in `docs.md`. It is recommended to review the [kubernetes docs on CRDs](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) as well.

Docs are added by creating a resource-specific readme in the directory of your mutator/validator (e.x. `pkg/resources/$GROUP/$GROUP_VERSION/$RESOURCE/$READABLE_RESOURCE.MD`).
These files should be named with a human-readable version of the CRD's name.
These files should be named with a human-readable version of the resource's name.
Running `go generate` will then aggregate these into the user-facing docs in the `docs.md` file.

## Webhooks
Expand Down
103 changes: 99 additions & 4 deletions docs.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,119 @@
# core/v1

## Namespace

### Validation Checks

Note: The kube-system namespace, unlike other namespaces, has a "failPolicy" of "ignore" on update calls.

#### PSA Label Validation

Validates that users who create or edit a PSA enforcement label on a namespace have the `updatepsa` verb on `projects` in `management.cattle.io/v3`. See the [upstream docs](https://kubernetes.io/docs/concepts/security/pod-security-admission/) for more information on the effect of these labels.

The following labels are considered relevant labels for PSA enforcement: `"pod-security.kubernetes.io/enforce", "pod-security.kubernetes.io/enforce-version", "pod-security.kubernetes.io/audit", "pod-security.kubernetes.io/audit-version", "pod-security.kubernetes.io/warn", "pod-security.kubernetes.io/warn-version"`.

# management.cattle.io/v3


## ClusterRoleTemplateBinding

### Validation Checks

#### Escalation Prevention

Users can only create/update ClusterRoleTemplateBindings which grant permissions to RoleTemplates with rights less than or equal to those they currently possess. This is to prevent privilege escalation.

#### Invalid Fields - Create

Users cannot create ClusterRoleTemplateBindings which violate the following constraints:
- Either a user subject (through "UserName" or "UserPrincipalName") or a group subject (through "GroupName" or "GroupPrincipalName") must be specified; both a user subject and group subject cannot be specified
- A "ClusterName" must be specified
- The roleTemplate indicated in "RoleTemplateName" must be:
- Valid (i.e. is an existing `roleTemplate` object in the `management.cattle.io/v3` apiGroup)
- Not locked (i.e. `roleTemplate.Locked` must be `false`)

#### Invalid Fields - Update

Users cannot update the following fields after creation:
- RoleTemplateName
- ClusterName

Users can update the following fields if they have not been set, but after they have been set they cannot be changed:
- UserName
- UserPrincipalName
- GroupName
- GroupPrincipalName

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

## GlobalRole

### Validation Checks

Note: all checks are bypassed if the GlobalRole is being deleted

#### Escalation Prevention
Users can only change GlobalRoles which have less permissions than they do. This is to prevents privilege escalation.

Users can only change GlobalRoles with rights less than or equal to those they currently possess. This is to prevent privilege escalation.

## GlobalRoleBinding

### Validation Checks

Note: all checks are bypassed if the GlobalRoleBinding is being deleted

#### Escalation Prevention

Users can only create/update GlobalRoleBindings with rights less than or equal to those they currently possess. This is to prevent privilege escalation.

#### Valid Global Role Reference

GlobalRoleBindings must refer to a valid global role (i.e. an existing `GlobalRole` object in the `management.cattle.io/v3` apiGroup).

## ProjectRoleTemplateBinding

### Validation Checks

#### Escalation Prevention

Users can only create/update ProjectRoleTemplateBindings with rights less than or equal to those they currently possess. This is to prevent privilege escalation.

#### Invalid Fields - Create

Users cannot create ProjectRoleTemplateBindings which violate the following constraints:
- Either a user subject (through "UserName" or "UserPrincipalName") or a group subject (through "GroupName" or "GroupPrincipalName") must be specified; both a user subject and group subject cannot be specified
- A "ProjectName" must be specified
- The roleTemplate indicated in "RoleTemplateName" must be:
- Valid (i.e. is an existing `roleTemplate` object in the `management.cattle.io/v3` apiGroup)
- Not locked (i.e. `roleTemplate.Locked` must be `false`)

#### Invalid Fields - Update

Users cannot update the following fields after creation:
- RoleTemplateName
- ProjectName

Users can update the following fields if they have not been set, but after they have been set they cannot be changed:
- UserName
- UserPrincipalName
- GroupName
- GroupPrincipalName

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

## RoleTemplate

### Validation Checks

Note: all checks are bypassed if the RoleTemplate is being deleted

#### Circular Reference

Circular references to webhooks (a inherits b, b inherits a) are not allowed. More specifically, if "roleTemplate1" is included in the `roleTemplateNames` of "roleTemplate2", then "roleTemplate2" must not be included in the `roleTemplateNames` of "roleTemplate1". This checks prevents the creation of roles whose end-state cannot be resolved.

#### Rules Without Verbs
Rules without verbs are not peritted. The `rules` included in a roleTemplate are of the same type as the rules used by standard kubernetes RBAC types (such as `Roles` from `rbac.authorization.k8s.io/v1`). Because of this, they inherit the same restrictions as these types, including this one.

Rules without verbs are not permitted. The `rules` included in a roleTemplate are of the same type as the rules used by standard kubernetes RBAC types (such as `Roles` from `rbac.authorization.k8s.io/v1`). Because of this, they inherit the same restrictions as these types, including this one.

#### Escalation Prevention
Users can only change RoleTemplates which have less permissions than they do. This prevents privilege escalation.

Users can only change RoleTemplates with rights less than or equal to those they currently possess. This prevents privilege escalation.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.2
golang.org/x/exp v0.0.0-20230206171751-46f607a40771
golang.org/x/text v0.8.0
golang.org/x/tools v0.7.0
k8s.io/api v0.25.5
k8s.io/apiextensions-apiserver v0.25.5
Expand Down Expand Up @@ -122,7 +123,6 @@ require (
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//go:generate go run pkg/codegen/cleanup/main.go
//go:generate go run pkg/codegen/main.go pkg/codegen/template.go pkg/codegen/docs.go
//go:generate go run ./pkg/codegen
package main

import (
Expand Down
101 changes: 63 additions & 38 deletions pkg/codegen/docs.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package main

import (
"bufio"
"bytes"
"fmt"
"os"
"path/filepath"
"strings"

"golang.org/x/exp/slices"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)

// docFileName defines the name of the files that will be aggregated into overall docs
Expand All @@ -19,8 +23,18 @@ type docFile struct {
version string
}

func generateDocs(resourcesBaseDir, outputFilePath string) error {
func generateDocs(resourcesBaseDir, outputFilePath string) (err error) {
outputFile, err := os.OpenFile(outputFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
defer func() {
closeErr := outputFile.Close()
if closeErr != nil {
if err != nil {
err = fmt.Errorf("%w, error when closing file %s", err, closeErr.Error())
} else {
err = closeErr
}
}
}()
if err != nil {
return err
}
Expand All @@ -33,34 +47,37 @@ func generateDocs(resourcesBaseDir, outputFilePath string) error {
newGroup := docFile.group
if newGroup != currentGroup {
// our group has changed, output a new group header
_, err = fmt.Fprintf(outputFile, "# %s/%s \n \n", docFile.group, docFile.version)
groupFormatString := "# %s/%s \n"
if currentGroup != "" {
groupFormatString = "\n" + groupFormatString
}
_, err = fmt.Fprintf(outputFile, groupFormatString, docFile.group, docFile.version)
if err != nil {
return fmt.Errorf("unable to write group header for %s/%s: %w", docFile.group, docFile.version, err)
}
currentGroup = newGroup
}

_, err = fmt.Fprintf(outputFile, "## %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)
}

lines := strings.Split(string(docFile.content), "\n")
for i, line := range lines {
newLine := line
if i < len(lines)-1 {
// last line doesn't need a newLine re-added
newLine += "\n"
}
if strings.HasPrefix(line, "#") {
scanner := bufio.NewScanner(bytes.NewReader(docFile.content))
for scanner.Scan() {
line := scanner.Bytes()
// even if the scanned line is empty, still need to output the newline
if len(line) != 0 && line[0] == '#' {
// this line is a markdown header. Since the group header is the top-level indent, indent this down one line
newLine = "#" + line
line = append([]byte{'#'}, line...)
}
_, err := outputFile.WriteString(newLine)
line = append(line, byte('\n'))
_, err := outputFile.Write(line)
if err != nil {
return fmt.Errorf("unable to write content for %s/%s.%s: %w", docFile.group, docFile.version, docFile.resource, err)
}

}

}
return nil
}
Expand All @@ -81,36 +98,44 @@ func getDocFiles(baseDir string) ([]docFile, error) {
return nil, err
}
docFiles = append(docFiles, subDocFiles...)
continue
}
if filepath.Ext(entry.Name()) == docFileExtension {
content, err := os.ReadFile(filepath.Join(baseDir, entry.Name()))
if err != nil {
return nil, fmt.Errorf("unable to read file content for %s: %w", entryPath, err)
}
var newDir, resource, version, group string
newDir, _ = filepath.Split(baseDir)
newDir, version = filepath.Split(newDir[:len(newDir)-1])
newDir, group = filepath.Split(newDir[:len(newDir)-1])
resource = strings.TrimSuffix(entry.Name(), docFileExtension)
if newDir == "" || resource == "" || version == "" || group == "" {
return nil, fmt.Errorf("unable to extract gvr from %s, got group %s, version %s, resource %s", baseDir, group, version, resource)
}
docFiles = append(docFiles, docFile{
content: content,
resource: resource,
group: group,
version: version,
})
if filepath.Ext(entry.Name()) != docFileExtension {
continue
}
content, err := os.ReadFile(filepath.Join(baseDir, entry.Name()))
if err != nil {
return nil, fmt.Errorf("unable to read file content for %s: %w", entryPath, err)
}
// lop off the last trailing new line to keep consistent spacing for later on
if content[len(content)-1] == '\n' {
content = content[:len(content)-1]
}
newDir, _ := filepath.Split(baseDir)
newDir, version := filepath.Split(newDir[:len(newDir)-1])
newDir, group := filepath.Split(newDir[:len(newDir)-1])
resource := strings.TrimSuffix(entry.Name(), docFileExtension)
if newDir == "" || resource == "" || version == "" || group == "" {
return nil, fmt.Errorf("unable to extract gvr from %s, got group %s, version %s, resource %s", baseDir, group, version, resource)
}
// group and version need to have a consistent case so that test.cattle.io/v3 and test.cattle.Io/V3 are grouped the same way
caser := cases.Lower(language.English)
docFiles = append(docFiles, docFile{
content: content,
resource: resource,
group: caser.String(group),
version: caser.String(version),
})
}
// if the groups differ, sort based on the group. If the groups are the same, sort based on the resource
slices.SortFunc(docFiles, func(a, b docFile) bool {
if a.group < b.group {
return true
} else if a.group == b.group {
return a.resource < b.resource
if a.group == b.group {
if a.resource == b.resource {
return a.version < b.version
}
return a.resource == b.resource
}
return false
return a.group < b.group
})

return docFiles, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/generated/controllers/management.cattle.io/factory.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a6c9cc2

Please sign in to comment.