mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-03-27 05:47:58 +00:00
Compare commits
19 Commits
maxgio92/f
...
helm-v0.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eba072c88d | ||
|
|
9d02fb39eb | ||
|
|
1df430e71b | ||
|
|
75525ac192 | ||
|
|
132ffd57ea | ||
|
|
7602114835 | ||
|
|
82996c1c83 | ||
|
|
ede96f5cf4 | ||
|
|
2fc1be8bfe | ||
|
|
e0dbf47723 | ||
|
|
fb68795e90 | ||
|
|
a026e2f00c | ||
|
|
413208e7fe | ||
|
|
2771b63c18 | ||
|
|
9a1520ff66 | ||
|
|
c304fb2438 | ||
|
|
6d56237e23 | ||
|
|
c32166ba45 | ||
|
|
e4ecbe30d1 |
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -9,10 +9,7 @@ assignees: ''
|
||||
|
||||
<!--
|
||||
Thanks for taking time reporting a Capsule bug!
|
||||
|
||||
We do our best to keep it reliable and working, so don't hesitate adding
|
||||
as many information as you can and keep in mind you can reach us on our
|
||||
Clastix Slack workspace: https://clastix.slack.com, #capsule channel.
|
||||
|
||||
-->
|
||||
|
||||
# Bug description
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Chat on Slack
|
||||
url: https://kubernetes.slack.com/archives/C03GETTJQRL
|
||||
about: Maybe chatting with the community can help
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -14,8 +14,6 @@ We're trying to build a community drive Open Source project, so don't
|
||||
hesitate proposing your enhancement ideas: keep in mind, since we would like
|
||||
to keep it as agnostic as possible, to motivate all your assumptions.
|
||||
|
||||
If you need to reach the maintainers, please join the Clastix Slack workspace:
|
||||
https://clastix.slack.com, #capsule channel.
|
||||
-->
|
||||
|
||||
# Describe the feature
|
||||
|
||||
10
.github/configs/ct.yaml
vendored
Normal file
10
.github/configs/ct.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
remote: origin
|
||||
target-branch: master
|
||||
chart-dirs:
|
||||
- charts
|
||||
helm-extra-args: "--timeout 600s"
|
||||
validate-chart-schema: false
|
||||
validate-maintainers: false
|
||||
validate-yaml: true
|
||||
exclude-deprecated: true
|
||||
check-version-increment: false
|
||||
43
.github/configs/lintconf.yaml
vendored
Normal file
43
.github/configs/lintconf.yaml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
---
|
||||
rules:
|
||||
braces:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 0
|
||||
min-spaces-inside-empty: -1
|
||||
max-spaces-inside-empty: -1
|
||||
brackets:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 0
|
||||
min-spaces-inside-empty: -1
|
||||
max-spaces-inside-empty: -1
|
||||
colons:
|
||||
max-spaces-before: 0
|
||||
max-spaces-after: 1
|
||||
commas:
|
||||
max-spaces-before: 0
|
||||
min-spaces-after: 1
|
||||
max-spaces-after: 1
|
||||
comments:
|
||||
require-starting-space: true
|
||||
min-spaces-from-content: 1
|
||||
document-end: disable
|
||||
document-start: disable # No --- to start a file
|
||||
empty-lines:
|
||||
max: 2
|
||||
max-start: 0
|
||||
max-end: 0
|
||||
hyphens:
|
||||
max-spaces-after: 1
|
||||
indentation:
|
||||
spaces: consistent
|
||||
indent-sequences: whatever # - list indentation will handle both indentation and without
|
||||
check-multi-line-strings: false
|
||||
key-duplicates: enable
|
||||
line-length: disable # Lines can be any length
|
||||
new-line-at-end-of-file: enable
|
||||
new-lines:
|
||||
type: unix
|
||||
trailing-spaces: enable
|
||||
truthy:
|
||||
level: warning
|
||||
5
.github/maintainers.yaml
vendored
5
.github/maintainers.yaml
vendored
@@ -16,3 +16,8 @@
|
||||
projects:
|
||||
- https://github.com/clastix/capsule
|
||||
- https://github.com/clastix/capsule-proxy
|
||||
- name: Oliver Bähler
|
||||
github: https://github.com/oliverbaehler
|
||||
company: Bedag Informatik AG
|
||||
projects:
|
||||
- https://github.com/clastix/capsule
|
||||
|
||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -40,6 +40,9 @@ jobs:
|
||||
- run: make installer
|
||||
- name: Checking if YAML installer file is not aligned
|
||||
run: if [[ $(git diff | wc -l) -gt 0 ]]; then echo ">>> Untracked generated files have not been committed" && git --no-pager diff && exit 1; fi
|
||||
- run: make apidoc
|
||||
- name: Checking if the CRDs documentation is not aligned
|
||||
run: if [[ $(git diff | wc -l) -gt 0 ]]; then echo ">>> CRDs generated documentation have not been committed" && git --no-pager diff && exit 1; fi
|
||||
- name: Checking if YAML installer generated untracked files
|
||||
run: test -z "$(git ls-files --others --exclude-standard 2> /dev/null)"
|
||||
- name: Checking if source code is not formatted
|
||||
|
||||
42
.github/workflows/helm.yml
vendored
42
.github/workflows/helm.yml
vendored
@@ -12,11 +12,53 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: 3.3.4
|
||||
- name: Linting Chart
|
||||
run: helm lint ./charts/capsule
|
||||
- name: Setup Chart Linting
|
||||
id: lint
|
||||
uses: helm/chart-testing-action@v2.3.0
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
run: |
|
||||
changed=$(ct list-changed --config ./.github/configs/ct.yaml)
|
||||
if [[ -n "$changed" ]]; then
|
||||
echo "::set-output name=changed::true"
|
||||
fi
|
||||
- name: Run chart-testing (lint)
|
||||
run: ct lint --debug --config ./.github/configs/ct.yaml --lint-conf ./.github/configs/lintconf.yaml
|
||||
- name: Run docs-testing (helm-docs)
|
||||
id: helm-docs
|
||||
run: |
|
||||
make helm-docs
|
||||
if [[ $(git diff --stat) != '' ]]; then
|
||||
echo -e '\033[0;31mDocumentation outdated! (Run make helm-docs locally and commit)\033[0m ❌'
|
||||
git diff --color
|
||||
exit 1
|
||||
else
|
||||
echo -e '\033[0;32mDocumentation up to date\033[0m ✔'
|
||||
fi
|
||||
# Create KIND Cluster
|
||||
- name: Create kind cluster
|
||||
uses: helm/kind-action@v1.2.0
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
# Install Required Operators/CRDs
|
||||
- name: Prepare Cluster Operators/CRDs
|
||||
run: |
|
||||
# Cert-Manager CRDs
|
||||
kubectl create -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.crds.yaml
|
||||
|
||||
# Prometheus CRDs
|
||||
kubectl create -f https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.58.0/bundle.yaml
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
# Install Charts
|
||||
- name: Run chart-testing (install)
|
||||
run: ct install --debug --config ./.github/configs/ct.yaml
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
release:
|
||||
if: startsWith(github.ref, 'refs/tags/helm-v')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,3 +30,5 @@ bin
|
||||
.DS_Store
|
||||
*.tgz
|
||||
|
||||
capsule
|
||||
|
||||
|
||||
@@ -3,3 +3,6 @@
|
||||
This is a list of companies that have adopted Capsule, feel free to open a Pull-Request to get yours listed.
|
||||
|
||||
## Adopters list (alphabetically)
|
||||
|
||||
### [Bedag Informatik AG](https://www.bedag.ch/)
|
||||

|
||||
|
||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement by contacting
|
||||
one of the [maintainers](https://raw.githubusercontent.com/clastix/capsule/master/.github/maintainers.yaml).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
45
Makefile
45
Makefile
@@ -78,6 +78,25 @@ manifests: controller-gen
|
||||
generate: controller-gen
|
||||
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
|
||||
|
||||
apidoc: apidocs-gen
|
||||
$(APIDOCS_GEN) crdoc --resources config/crd/bases --output docs/content/general/tenant-crd.md --template docs/template/reference-cr.tmpl
|
||||
|
||||
# Helm
|
||||
SRC_ROOT = $(shell git rev-parse --show-toplevel)
|
||||
|
||||
helm-docs: HELMDOCS_VERSION := v1.11.0
|
||||
helm-docs: docker
|
||||
@docker run -v "$(SRC_ROOT):/helm-docs" jnorwood/helm-docs:$(HELMDOCS_VERSION) --chart-search-root /helm-docs
|
||||
|
||||
helm-lint: docker
|
||||
@docker run -v "$(SRC_ROOT):/workdir" --entrypoint /bin/sh quay.io/helmpack/chart-testing:v3.3.1 -c "cd /workdir && ct lint --config .github/configs/ct.yaml --lint-conf .github/configs/lintconf.yaml --all --debug"
|
||||
|
||||
docker:
|
||||
@hash docker 2>/dev/null || {\
|
||||
echo "You need docker" &&\
|
||||
exit 1;\
|
||||
}
|
||||
|
||||
# Setup development env
|
||||
# Usage:
|
||||
# LAPTOP_HOST_IP=<YOUR_LAPTOP_IP> make dev-setup
|
||||
@@ -115,7 +134,7 @@ dev-setup:
|
||||
export CA_BUNDLE=`openssl base64 -in /tmp/k8s-webhook-server/serving-certs/tls.crt | tr -d '\n'`; \
|
||||
kubectl patch MutatingWebhookConfiguration capsule-mutating-webhook-configuration \
|
||||
--type='json' -p="[\
|
||||
{'op': 'replace', 'path': '/webhooks/0/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/mutate-v1-namespace-owner-reference\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
{'op': 'replace', 'path': '/webhooks/0/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/namespace-owner-reference\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
]" && \
|
||||
kubectl patch ValidatingWebhookConfiguration capsule-validating-webhook-configuration \
|
||||
--type='json' -p="[\
|
||||
@@ -123,11 +142,11 @@ dev-setup:
|
||||
{'op': 'replace', 'path': '/webhooks/1/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/ingresses\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/2/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/namespaces\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/3/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/networkpolicies\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/4/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/pods\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/5/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/persistentvolumeclaims\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/6/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/services\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/7/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/tenants\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/8/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/nodes\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
{'op': 'replace', 'path': '/webhooks/4/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/nodes\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/5/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/pods\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/6/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/persistentvolumeclaims\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/7/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/services\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/8/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/tenants\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
]";
|
||||
|
||||
# Build the docker image
|
||||
@@ -147,9 +166,13 @@ CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
|
||||
controller-gen: ## Download controller-gen locally if necessary.
|
||||
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.5.0)
|
||||
|
||||
APIDOCS_GEN = $(shell pwd)/bin/crdoc
|
||||
apidocs-gen: ## Download crdoc locally if necessary.
|
||||
$(call go-install-tool,$(APIDOCS_GEN),fybrik.io/crdoc@latest)
|
||||
|
||||
GINKGO = $(shell pwd)/bin/ginkgo
|
||||
ginkgo: ## Download ginkgo locally if necessary.
|
||||
$(call go-install-tool,$(KUSTOMIZE),github.com/onsi/ginkgo/ginkgo@v1.16.5)
|
||||
$(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/ginkgo@v1.16.5)
|
||||
|
||||
KUSTOMIZE = $(shell pwd)/bin/kustomize
|
||||
kustomize: ## Download kustomize locally if necessary.
|
||||
@@ -198,6 +221,9 @@ golint:
|
||||
# Running e2e tests in a KinD instance
|
||||
.PHONY: e2e
|
||||
e2e/%: ginkgo
|
||||
$(MAKE) e2e-build/$* && $(MAKE) e2e-exec || $(MAKE) e2e-destroy
|
||||
|
||||
e2e-build/%:
|
||||
kind create cluster --name capsule --image=kindest/node:$*
|
||||
make docker-build
|
||||
kind load docker-image --nodes capsule-control-plane --name capsule $(IMG)
|
||||
@@ -213,5 +239,10 @@ e2e/%: ginkgo
|
||||
--set 'manager.readinessProbe.failureThreshold=10' \
|
||||
capsule \
|
||||
./charts/capsule
|
||||
|
||||
e2e-exec:
|
||||
$(GINKGO) -v -tags e2e ./e2e
|
||||
|
||||
e2e-destroy:
|
||||
kind delete cluster --name capsule
|
||||
|
||||
|
||||
14
README.md
14
README.md
@@ -72,12 +72,20 @@ Capsule is Open Source with Apache 2 license and any contribution is welcome.
|
||||
|
||||
## Chart Development
|
||||
|
||||
The documentation for each chart is done with [helm-docs](https://github.com/norwoodj/helm-docs). This way we can ensure that values are consistent with the chart documentation.
|
||||
### Chart Linting
|
||||
|
||||
We have a script on the repository which will execute the helm-docs docker container, so that you don't have to worry about downloading the binary etc. Simply execute the script (Bash compatible):
|
||||
The chart is linted with [ct](https://github.com/helm/chart-testing). You can run the linter locally with this command:
|
||||
|
||||
```
|
||||
bash scripts/helm-docs.sh
|
||||
make helm-lint
|
||||
```
|
||||
|
||||
### Chart Documentation
|
||||
|
||||
The documentation for each chart is done with [helm-docs](https://github.com/norwoodj/helm-docs). This way we can ensure that values are consistent with the chart documentation. Run this anytime you make changes to a `values.yaml` file:
|
||||
|
||||
```
|
||||
make helm-docs
|
||||
```
|
||||
|
||||
## Community
|
||||
|
||||
@@ -21,8 +21,8 @@ sources:
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
version: 0.1.11
|
||||
version: 0.1.12
|
||||
|
||||
# This is the version number of the application being deployed.
|
||||
# This version number should be incremented each time you make changes to the application.
|
||||
appVersion: 0.1.2
|
||||
appVersion: 0.1.3
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
docs: HELMDOCS_VERSION := v1.8.1
|
||||
docs: docker
|
||||
@docker run --rm -v "$$(pwd):/helm-docs" -u $$(id -u) jnorwood/helm-docs:$(HELMDOCS_VERSION)
|
||||
|
||||
docker:
|
||||
@hash docker 2>/dev/null || {\
|
||||
echo "You need docker" &&\
|
||||
exit 1;\
|
||||
}
|
||||
@@ -64,8 +64,8 @@ Here the values you can override:
|
||||
|-----|------|---------|-------------|
|
||||
| affinity | object | `{}` | Set affinity rules for the Capsule pod |
|
||||
| certManager.generateCertificates | bool | `false` | Specifies whether capsule webhooks certificates should be generated using cert-manager |
|
||||
| customAnnotations | object | `{}` | Additional annotations which will be added to all resources created by Capsule helm chart |
|
||||
| customLabels | object | `{}` | Additional labels which will be added to all resources created by Capsule helm chart |
|
||||
| customAnnotations | object | `{}` | Additional annotations which will be added to all resources created by Capsule helm chart |
|
||||
| customLabels | object | `{}` | Additional labels which will be added to all resources created by Capsule helm chart |
|
||||
| jobs.image.pullPolicy | string | `"IfNotPresent"` | Set the image pull policy of the helm chart job |
|
||||
| jobs.image.repository | string | `"quay.io/clastix/kubectl"` | Set the image repository of the helm chart job |
|
||||
| jobs.image.tag | string | `""` | Set the image tag of the helm chart job |
|
||||
@@ -88,18 +88,18 @@ Here the values you can override:
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| manager.hostNetwork | bool | `false` | Specifies if the container should be started in hostNetwork mode. Required for use in some managed kubernetes clusters (such as AWS EKS) with custom CNI (such as calico), because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working |
|
||||
| manager.hostNetwork | bool | `false` | Specifies if the container should be started in hostNetwork mode. Required for use in some managed kubernetes clusters (such as AWS EKS) with custom CNI (such as calico), because control-plane managed by AWS cannot communicate with pods' IP CIDR and admission webhooks are not working |
|
||||
| manager.image.pullPolicy | string | `"IfNotPresent"` | Set the image pull policy. |
|
||||
| manager.image.repository | string | `"clastix/capsule"` | Set the image repository of the capsule. |
|
||||
| manager.image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
|
||||
| manager.imagePullSecrets | list | `[]` | Configuration for `imagePullSecrets` so that you can use a private images registry. |
|
||||
| manager.kind | string | `"Deployment"` | Set the controller deployment mode as `Deployment` or `DaemonSet`. |
|
||||
| manager.livenessProbe | object | `{"httpGet":{"path":"/healthz","port":10080}}` | Configure the liveness probe using Deployment probe spec |
|
||||
| manager.options.capsuleUserGroups | list | `["capsule.clastix.io"]` | Override the Capsule user groups |
|
||||
| manager.imagePullSecrets | list | `[]` | Configuration for `imagePullSecrets` so that you can use a private images registry. |
|
||||
| manager.kind | string | `"Deployment"` | Set the controller deployment mode as `Deployment` or `DaemonSet`. |
|
||||
| manager.livenessProbe | object | `{"httpGet":{"path":"/healthz","port":10080}}` | Configure the liveness probe using Deployment probe spec |
|
||||
| manager.options.capsuleUserGroups | list | `["capsule.clastix.io"]` | Override the Capsule user groups |
|
||||
| manager.options.forceTenantPrefix | bool | `false` | Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash |
|
||||
| manager.options.generateCertificates | bool | `true` | Specifies whether capsule webhooks certificates should be generated by capsule operator |
|
||||
| manager.options.logLevel | string | `"4"` | Set the log verbosity of the capsule with a value from 1 to 10 |
|
||||
| manager.options.protectedNamespaceRegex | string | `""` | If specified, disallows creation of namespaces matching the passed regexp |
|
||||
| manager.options.protectedNamespaceRegex | string | `""` | If specified, disallows creation of namespaces matching the passed regexp |
|
||||
| manager.readinessProbe | object | `{"httpGet":{"path":"/readyz","port":10080}}` | Configure the readiness probe using Deployment probe spec |
|
||||
| manager.resources.limits.cpu | string | `"200m"` | |
|
||||
| manager.resources.limits.memory | string | `"128Mi"` | |
|
||||
|
||||
@@ -14,7 +14,7 @@ tls:
|
||||
# Manager Options
|
||||
manager:
|
||||
|
||||
# -- Set the controller deployment mode as `Deployment` or `DaemonSet`.
|
||||
# -- Set the controller deployment mode as `Deployment` or `DaemonSet`.
|
||||
kind: Deployment
|
||||
|
||||
image:
|
||||
@@ -25,7 +25,7 @@ manager:
|
||||
# -- Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ''
|
||||
|
||||
# -- Configuration for `imagePullSecrets` so that you can use a private images registry.
|
||||
# -- Configuration for `imagePullSecrets` so that you can use a private images registry.
|
||||
imagePullSecrets: []
|
||||
|
||||
# -- Specifies if the container should be started in hostNetwork mode.
|
||||
@@ -41,19 +41,19 @@ manager:
|
||||
logLevel: '4'
|
||||
# -- Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash
|
||||
forceTenantPrefix: false
|
||||
# -- Override the Capsule user groups
|
||||
# -- Override the Capsule user groups
|
||||
capsuleUserGroups: ["capsule.clastix.io"]
|
||||
# -- If specified, disallows creation of namespaces matching the passed regexp
|
||||
# -- If specified, disallows creation of namespaces matching the passed regexp
|
||||
protectedNamespaceRegex: ""
|
||||
# -- Specifies whether capsule webhooks certificates should be generated by capsule operator
|
||||
generateCertificates: true
|
||||
|
||||
# -- Configure the liveness probe using Deployment probe spec
|
||||
# -- Configure the liveness probe using Deployment probe spec
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 10080
|
||||
|
||||
|
||||
# -- Configure the readiness probe using Deployment probe spec
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
@@ -75,7 +75,7 @@ podAnnotations: {}
|
||||
# scheduler.alpha.kubernetes.io/critical-pod: ''
|
||||
|
||||
# -- Set the priority class name of the Capsule pod
|
||||
priorityClassName: '' #system-cluster-critical
|
||||
priorityClassName: '' # system-cluster-critical
|
||||
|
||||
# -- Set the node selector for the Capsule pod
|
||||
nodeSelector: {}
|
||||
@@ -83,10 +83,10 @@ nodeSelector: {}
|
||||
|
||||
# -- Set list of tolerations for the Capsule pod
|
||||
tolerations: []
|
||||
#- key: CriticalAddonsOnly
|
||||
# operator: Exists
|
||||
#- effect: NoSchedule
|
||||
# key: node-role.kubernetes.io/master
|
||||
# - key: CriticalAddonsOnly
|
||||
# operator: Exists
|
||||
# - effect: NoSchedule
|
||||
# key: node-role.kubernetes.io/master
|
||||
|
||||
# -- Set the replica count for capsule pod
|
||||
replicaCount: 1
|
||||
@@ -120,10 +120,10 @@ certManager:
|
||||
# -- Specifies whether capsule webhooks certificates should be generated using cert-manager
|
||||
generateCertificates: false
|
||||
|
||||
# -- Additional labels which will be added to all resources created by Capsule helm chart
|
||||
# -- Additional labels which will be added to all resources created by Capsule helm chart
|
||||
customLabels: {}
|
||||
|
||||
# -- Additional annotations which will be added to all resources created by Capsule helm chart
|
||||
# -- Additional annotations which will be added to all resources created by Capsule helm chart
|
||||
customAnnotations: {}
|
||||
|
||||
# Webhooks configurations
|
||||
|
||||
@@ -1411,7 +1411,7 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: clastix/capsule:v0.1.2
|
||||
image: clastix/capsule:v0.1.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: manager
|
||||
ports:
|
||||
|
||||
@@ -7,4 +7,4 @@ kind: Kustomization
|
||||
images:
|
||||
- name: controller
|
||||
newName: clastix/capsule
|
||||
newTag: v0.1.2
|
||||
newTag: v0.1.3
|
||||
|
||||
@@ -38,7 +38,10 @@ func (r *abstractServiceLabelsReconciler) InjectClient(c client.Client) error {
|
||||
func (r *abstractServiceLabelsReconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
|
||||
tenant, err := r.getTenant(ctx, request.NamespacedName, r.client)
|
||||
if err != nil {
|
||||
if errors.As(err, &NonTenantObjectError{}) || errors.As(err, &NoServicesMetadataError{}) {
|
||||
noTenantObjError := &NonTenantObjectError{}
|
||||
noSvcMetaError := &NoServicesMetadataError{}
|
||||
|
||||
if errors.As(err, &noTenantObjError) || errors.As(err, &noSvcMetaError) {
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
"github.com/clastix/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
// Ensuring all annotations are applied to each Namespace handled by the Tenant.
|
||||
@@ -72,11 +73,7 @@ func (r *Manager) syncNamespaceMetadata(ctx context.Context, namespace string, t
|
||||
}
|
||||
|
||||
if tnt.Spec.NodeSelector != nil {
|
||||
var selector []string
|
||||
for k, v := range tnt.Spec.NodeSelector {
|
||||
selector = append(selector, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
annotations["scheduler.alpha.kubernetes.io/node-selector"] = strings.Join(selector, ",")
|
||||
annotations = utils.BuildNodeSelector(tnt, annotations)
|
||||
}
|
||||
|
||||
if tnt.Spec.IngressOptions.AllowedClasses != nil {
|
||||
|
||||
@@ -115,6 +115,12 @@ upstream https://github.com/clastix/capsule.git (fetch)
|
||||
upstream https://github.com/clastix/capsule.git (push)
|
||||
```
|
||||
|
||||
Pull all tags
|
||||
|
||||
```
|
||||
$ git fetch --all && git pull upstream
|
||||
```
|
||||
|
||||
Build and deploy:
|
||||
|
||||
```shell
|
||||
|
||||
@@ -4,143 +4,9 @@ Reference document for Capsule Operator configuration
|
||||
|
||||
## Custom Resource Definition
|
||||
|
||||
Capsule operator uses a Custom Resources Definition (CRD) for _Tenants_. Tenants are cluster wide resources, so you need cluster level permissions to work with tenants. You can learn about tenant CRD by the `kubectl explain` command:
|
||||
|
||||
```
|
||||
kubectl explain tenant
|
||||
|
||||
KIND: Tenant
|
||||
VERSION: capsule.clastix.io/v1beta1
|
||||
|
||||
DESCRIPTION:
|
||||
Tenant is the Schema for the tenants API
|
||||
|
||||
FIELDS:
|
||||
apiVersion <string>
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value,
|
||||
and may reject unrecognized values. More info:
|
||||
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
|
||||
kind <string>
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated. In CamelCase. More info:
|
||||
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
|
||||
metadata <Object>
|
||||
Standard object's metadata. More info:
|
||||
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
|
||||
spec <Object>
|
||||
TenantSpec defines the desired state of Tenant
|
||||
|
||||
status <Object>
|
||||
Returns the observed state of the Tenant
|
||||
```
|
||||
|
||||
For Tenant spec:
|
||||
|
||||
```
|
||||
kubectl explain tenant.spec
|
||||
|
||||
KIND: Tenant
|
||||
VERSION: capsule.clastix.io/v1beta1
|
||||
|
||||
RESOURCE: spec <Object>
|
||||
|
||||
DESCRIPTION:
|
||||
TenantSpec defines the desired state of Tenant
|
||||
|
||||
FIELDS:
|
||||
additionalRoleBindings <[]Object>
|
||||
Specifies additional RoleBindings assigned to the Tenant. Capsule will
|
||||
ensure that all namespaces in the Tenant always contain the RoleBinding for
|
||||
the given ClusterRole. Optional.
|
||||
|
||||
containerRegistries <Object>
|
||||
Specifies the trusted Image Registries assigned to the Tenant. Capsule
|
||||
assures that all Pods resources created in the Tenant can use only one of
|
||||
the allowed trusted registries. Optional.
|
||||
|
||||
imagePullPolicies <[]string>
|
||||
Specify the allowed values for the imagePullPolicies option in Pod
|
||||
resources. Capsule assures that all Pod resources created in the Tenant can
|
||||
use only one of the allowed policy. Optional.
|
||||
|
||||
ingressOptions <Object>
|
||||
Specifies options for the Ingress resources, such as allowed hostnames and
|
||||
IngressClass. Optional.
|
||||
|
||||
limitRanges <Object>
|
||||
Specifies the resource min/max usage restrictions to the Tenant. The assigned
|
||||
values are inherited by any namespace created in the Tenant. Optional.
|
||||
|
||||
namespaceOptions <Object>
|
||||
Specifies options for the Namespaces, such as additional metadata or
|
||||
maximum number of namespaces allowed for that Tenant. Once the namespace
|
||||
quota assigned to the Tenant has been reached, the Tenant owner cannot
|
||||
create further namespaces. Optional.
|
||||
|
||||
networkPolicies <Object>
|
||||
Specifies the NetworkPolicies assigned to the Tenant. The assigned
|
||||
NetworkPolicies are inherited by any namespace created in the Tenant.
|
||||
Optional.
|
||||
|
||||
nodeSelector <map[string]string>
|
||||
Specifies the label to control the placement of pods on a given pool of
|
||||
worker nodes. All namesapces created within the Tenant will have the node
|
||||
selector annotation. This annotation tells the Kubernetes scheduler to
|
||||
place pods on the nodes having the selector label. Optional.
|
||||
|
||||
owners <[]Object> -required-
|
||||
Specifies the owners of the Tenant. Mandatory.
|
||||
|
||||
priorityClasses <Object>
|
||||
Specifies the allowed priorityClasses assigned to the Tenant. Capsule
|
||||
assures that all pods created in the Tenant can use only one
|
||||
of the allowed priorityClasses. Optional.
|
||||
|
||||
resourceQuotas <Object>
|
||||
Specifies a list of ResourceQuota resources assigned to the Tenant. The
|
||||
assigned values are inherited by any namespace created in the Tenant. The
|
||||
Capsule operator aggregates ResourceQuota at Tenant level, so that the hard
|
||||
quota is never crossed for the given Tenant. This permits the Tenant owner
|
||||
to consume resources in the Tenant regardless of the namespace. Optional.
|
||||
|
||||
serviceOptions <Object>
|
||||
Specifies options for the Service, such as additional metadata or block of
|
||||
certain type of Services. Optional.
|
||||
|
||||
storageClasses <Object>
|
||||
Specifies the allowed StorageClasses assigned to the Tenant. Capsule
|
||||
assures that all PersistentVolumeClaim resources created in the Tenant can
|
||||
use only one of the allowed StorageClasses. Optional.
|
||||
```
|
||||
|
||||
and Tenant status:
|
||||
|
||||
```
|
||||
kubectl explain tenant.status
|
||||
KIND: Tenant
|
||||
VERSION: capsule.clastix.io/v1beta1
|
||||
|
||||
RESOURCE: status <Object>
|
||||
|
||||
DESCRIPTION:
|
||||
Returns the observed state of the Tenant
|
||||
|
||||
FIELDS:
|
||||
namespaces <[]string>
|
||||
List of namespaces assigned to the Tenant.
|
||||
|
||||
size <integer> -required-
|
||||
How many namespaces are assigned to the Tenant.
|
||||
|
||||
state <string> -required-
|
||||
The operational state of the Tenant. Possible values are "Active",
|
||||
"Cordoned".
|
||||
```
|
||||
Capsule operator uses a Custom Resources Definition (CRD) for _Tenants_.
|
||||
Tenants are cluster wide resources, so you need cluster level permissions to work with tenants.
|
||||
You can learn about tenant CRDs in the following [section](./tenant-crd)
|
||||
|
||||
## Capsule Configuration
|
||||
|
||||
@@ -239,4 +105,4 @@ capsule-system secret/capsule-tls
|
||||
capsule-system service/capsule-controller-manager-metrics-service
|
||||
capsule-system service/capsule-webhook-service
|
||||
capsule-system deployment.apps/capsule-controller-manager
|
||||
```
|
||||
```
|
||||
3378
docs/content/general/tenant-crd.md
Normal file
3378
docs/content/general/tenant-crd.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -139,15 +139,15 @@ metadata:
|
||||
name: oil
|
||||
spec:
|
||||
owners:
|
||||
- name: system:serviceaccount:default:robot
|
||||
- name: system:serviceaccount:tenant-system:robot
|
||||
kind: ServiceAccount
|
||||
EOF
|
||||
```
|
||||
|
||||
Bill can create a Service Account called `robot`, for example, in the `default` namespace and leave it to act as Tenant Owner of the `oil` tenant
|
||||
Bill can create a Service Account called `robot`, for example, in the `tenant-system` namespace and leave it to act as Tenant Owner of the `oil` tenant
|
||||
|
||||
```
|
||||
kubectl --as system:serviceaccount:default:robot --as-group capsule.clastix.io auth can-i create namespaces
|
||||
kubectl --as system:serviceaccount:tenant-system:robot --as-group capsule.clastix.io auth can-i create namespaces
|
||||
yes
|
||||
```
|
||||
|
||||
@@ -160,7 +160,7 @@ metadata:
|
||||
name: default
|
||||
spec:
|
||||
userGroups:
|
||||
- system:serviceaccounts:default
|
||||
- system:serviceaccounts:tenant-system
|
||||
```
|
||||
|
||||
since each service account in a namespace is a member of following group:
|
||||
@@ -169,6 +169,23 @@ since each service account in a namespace is a member of following group:
|
||||
system:serviceaccounts:{service-account-namespace}
|
||||
```
|
||||
|
||||
You can change the CapsuleConfiguration at install time with a helm parameter:
|
||||
```
|
||||
helm upgrade -i \
|
||||
capsule \
|
||||
clastix/capsule \
|
||||
-n capsule-system \
|
||||
--set manager.options.capsuleUserGroups=system:serviceaccounts:tenant-system \
|
||||
--create-namespace
|
||||
```
|
||||
|
||||
Or after installation:
|
||||
```
|
||||
kubectl patch capsuleconfigurations default \
|
||||
--patch '{"spec":{"userGroups":["capsule.clastix.io","system:serviceaccounts:tenant-system"]}}' \
|
||||
--type=merge
|
||||
```
|
||||
|
||||
> Please, pay attention when setting a service account acting as tenant owner. Make sure you're not using the group `system:serviceaccounts` or the group `system:serviceaccounts:{capsule-namespace}` as Capsule group, otherwise you'll create a short-circuit in the Capsule controller, being Capsule itself controlled by a serviceaccount.
|
||||
|
||||
### Roles assigned to Tenant Owners
|
||||
|
||||
@@ -279,35 +279,6 @@ this is the required set of resources to setup a Tenant:
|
||||
userGroups:
|
||||
- system:serviceaccounts:my-tenant
|
||||
```
|
||||
- Additional `ClusterRole` with related `ClusterRoleBinding` that allows to `PATCH` requests on Namespaces, besides `CREATE`. Flux kustomize controller will `kubectl-apply` resources:
|
||||
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: capsule-namespace-provisioner-gitops
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- patch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: capsule-namespace-provisioner-gitops-my-tenant
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: capsule-namespace-provisioner-gitops
|
||||
subjects:
|
||||
- apiGroup: rbac.authorization.k8s.io
|
||||
kind: Group
|
||||
name: system:serviceaccounts:my-tenant
|
||||
```
|
||||
|
||||
- Additional `ClusterRole` with related `ClusterRoleBinding` that allows the Tenant GitOps Reconciler to impersonate his own `User` (e.g. `system:serviceaccount:my-tenant:gitops-reconciler`):
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
||||
@@ -68,7 +68,7 @@ velero create backup oil-namespaces \
|
||||
--include-namespaces oil-production,oil-development,oil-marketing
|
||||
```
|
||||
|
||||
resulting ti the following Velero object:
|
||||
resulting to the following Velero object:
|
||||
|
||||
```yaml
|
||||
apiVersion: velero.io/v1
|
||||
@@ -122,4 +122,4 @@ NAME STATE NAMESPACE QUOTA NAMESPACE COUNT NODE SELECTOR AGE
|
||||
gas active 9 5 {"pool":"gas"} 44m
|
||||
solar active 9 8 {"pool":"solar"} 43m
|
||||
oil active 9 3 # <<< {"pool":"oil"} 12s
|
||||
```
|
||||
```
|
||||
|
||||
@@ -37,6 +37,10 @@ module.exports = function (api) {
|
||||
label: 'References',
|
||||
path: '/docs/general/references'
|
||||
},
|
||||
{
|
||||
label: 'CRDs APIs',
|
||||
path: '/docs/general/tenant-crd'
|
||||
},
|
||||
{
|
||||
label: 'Multi-Tenant Benchmark',
|
||||
path: '/docs/general/mtb'
|
||||
|
||||
18
docs/package-lock.json
generated
18
docs/package-lock.json
generated
@@ -4472,9 +4472,9 @@
|
||||
}
|
||||
},
|
||||
"defined": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
|
||||
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz",
|
||||
"integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
|
||||
"dev": true
|
||||
},
|
||||
"delayed-stream": {
|
||||
@@ -8144,9 +8144,9 @@
|
||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -10842,9 +10842,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
||||
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
||||
"version": "8.4.19",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
|
||||
"integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
|
||||
111
docs/template/reference-cr.tmpl
vendored
Normal file
111
docs/template/reference-cr.tmpl
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
<style>
|
||||
table {
|
||||
border: solid;
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
th, td {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
border: solid;
|
||||
}
|
||||
tr:hover {background-color: coral;}
|
||||
sup {
|
||||
font-size: 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
# API Reference
|
||||
|
||||
Packages:
|
||||
{{range .Groups}}
|
||||
- [{{.Group}}/{{.Version}}](#{{ anchorize (printf "%s/%s" .Group .Version) }})
|
||||
{{- end -}}{{/* range .Groups */}}
|
||||
|
||||
{{- range .Groups }}
|
||||
{{- $group := . }}
|
||||
|
||||
# {{.Group}}/{{.Version}}
|
||||
|
||||
Resource Types:
|
||||
{{range .Kinds}}
|
||||
- [{{.Name}}](#{{ anchorize .Name }})
|
||||
{{end}}{{/* range .Kinds */}}
|
||||
|
||||
{{range .Kinds}}
|
||||
{{$kind := .}}
|
||||
## {{.Name}}
|
||||
|
||||
{{range .Types}}
|
||||
|
||||
{{if not .IsTopLevel}}
|
||||
### {{.Name}}
|
||||
{{end}}
|
||||
|
||||
|
||||
{{.Description}}
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{- if .IsTopLevel -}}
|
||||
<tr>
|
||||
<td><b>apiVersion</b></td>
|
||||
<td>string</td>
|
||||
<td>{{$group.Group}}/{{$group.Version}}</td>
|
||||
<td>true</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>kind</b></td>
|
||||
<td>string</td>
|
||||
<td>{{$kind.Name}}</td>
|
||||
<td>true</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b><a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#objectmeta-v1-meta">metadata</a></b></td>
|
||||
<td>object</td>
|
||||
<td>Refer to the Kubernetes API documentation for the fields of the `metadata` field.</td>
|
||||
<td>true</td>
|
||||
</tr>
|
||||
{{- end -}}
|
||||
{{- range .Fields -}}
|
||||
<tr>
|
||||
<td><b>{{if .TypeKey}}<a href="#{{.TypeKey}}">{{.Name}}</a>{{else}}{{.Name}}{{end}}</b></td>
|
||||
<td>{{.Type}}</td>
|
||||
<td>
|
||||
{{.Description}}<br/>
|
||||
{{- if or .Schema.Format .Schema.Enum .Schema.Default .Schema.Minimum .Schema.Maximum }}
|
||||
<br/>
|
||||
{{- end}}
|
||||
{{- if .Schema.Format }}
|
||||
<i>Format</i>: {{ .Schema.Format }}<br/>
|
||||
{{- end }}
|
||||
{{- if .Schema.Enum }}
|
||||
<i>Enum</i>: {{ .Schema.Enum | toStrings | join ", " }}<br/>
|
||||
{{- end }}
|
||||
{{- if .Schema.Default }}
|
||||
<i>Default</i>: {{ .Schema.Default }}<br/>
|
||||
{{- end }}
|
||||
{{- if .Schema.Minimum }}
|
||||
<i>Minimum</i>: {{ .Schema.Minimum }}<br/>
|
||||
{{- end }}
|
||||
{{- if .Schema.Maximum }}
|
||||
<i>Maximum</i>: {{ .Schema.Maximum }}<br/>
|
||||
{{- end }}
|
||||
</td>
|
||||
<td>{{.Required}}</td>
|
||||
</tr>
|
||||
{{- end -}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{- end}}{{/* range .Types */}}
|
||||
{{- end}}{{/* range .Kinds */}}
|
||||
{{- end}}{{/* range .Groups */}}
|
||||
@@ -2634,7 +2634,7 @@ component-emitter@^1.2.1:
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
|
||||
concat-stream@^1.5.0:
|
||||
version "1.6.2"
|
||||
@@ -6368,9 +6368,9 @@ minimalistic-crypto-utils@^1.0.1:
|
||||
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
|
||||
146
e2e/sa_prevent_privilege_escalation_test.go
Normal file
146
e2e/sa_prevent_privilege_escalation_test.go
Normal file
@@ -0,0 +1,146 @@
|
||||
//go:build e2e
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("trying to escalate from a Tenant Namespace ServiceAccount", func() {
|
||||
tnt := &capsulev1beta1.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sa-privilege-escalation",
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Name: "mario",
|
||||
Kind: "User",
|
||||
},
|
||||
},
|
||||
NodeSelector: map[string]string{
|
||||
"kubernetes.io/os": "linux",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ns := NewNamespace("attack")
|
||||
|
||||
JustBeforeEach(func() {
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.TODO(), tnt)
|
||||
}).Should(Succeed())
|
||||
|
||||
NamespaceCreation(ns, tnt.Spec.Owners[0], defaultTimeoutInterval).Should(Succeed())
|
||||
TenantNamespaceList(tnt, defaultTimeoutInterval).Should(ContainElement(ns.GetName()))
|
||||
})
|
||||
|
||||
JustAfterEach(func() {
|
||||
Expect(k8sClient.Delete(context.TODO(), tnt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("should block Namespace changes", func() {
|
||||
role := rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns-update-role",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
Verbs: []string{"update"},
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"namespaces"},
|
||||
ResourceNames: []string{ns.GetName()},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.Background(), &role)
|
||||
}).Should(Succeed())
|
||||
|
||||
rolebinding := rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "attacker-rolebinding",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
Name: "attacker",
|
||||
Namespace: ns.GetName(),
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "Role",
|
||||
Name: role.GetName(),
|
||||
},
|
||||
}
|
||||
|
||||
EventuallyCreation(func() error {
|
||||
return k8sClient.Create(context.Background(), &rolebinding)
|
||||
}).Should(Succeed())
|
||||
|
||||
c, err := config.GetConfig()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
c.Impersonate.Groups = []string{"system:serviceaccounts"}
|
||||
c.Impersonate.UserName = fmt.Sprintf("system:serviceaccount:%s:%s", rolebinding.Subjects[0].Namespace, rolebinding.Subjects[0].Name)
|
||||
saClient, err := kubernetes.NewForConfig(c)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Changing Owner Reference is forbidden
|
||||
Consistently(func() error {
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.GetName()}, ns); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ns.OwnerReferences[0].UID = uuid.NewUUID()
|
||||
|
||||
_, err = saClient.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
|
||||
|
||||
return err
|
||||
}, 10*time.Second, time.Second).ShouldNot(Succeed())
|
||||
// Removing Owner Reference is forbidden
|
||||
Consistently(func() error {
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.GetName()}, ns); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ns.SetOwnerReferences(nil)
|
||||
|
||||
_, err = saClient.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
|
||||
|
||||
return err
|
||||
}, 10*time.Second, time.Second).ShouldNot(Succeed())
|
||||
// Breaking nodeSelector is forbidden
|
||||
Consistently(func() error {
|
||||
if err := k8sClient.Get(context.Background(), types.NamespacedName{Name: ns.GetName()}, ns); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ns.SetAnnotations(map[string]string{
|
||||
"scheduler.alpha.kubernetes.io/node-selector": "kubernetes.io/os=forbidden",
|
||||
})
|
||||
|
||||
_, err = saClient.CoreV1().Namespaces().Update(context.Background(), ns, metav1.UpdateOptions{})
|
||||
|
||||
return err
|
||||
}, 10*time.Second, time.Second).ShouldNot(Succeed())
|
||||
})
|
||||
})
|
||||
2
main.go
2
main.go
@@ -205,7 +205,7 @@ func main() {
|
||||
route.Service(service.Handler()),
|
||||
route.NetworkPolicy(utils.InCapsuleGroups(cfg, networkpolicy.Handler())),
|
||||
route.Tenant(tenant.NameHandler(), tenant.RoleBindingRegexHandler(), tenant.IngressClassRegexHandler(), tenant.StorageClassRegexHandler(), tenant.ContainerRegistryRegexHandler(), tenant.HostnameRegexHandler(), tenant.FreezedEmitter(), tenant.ServiceAccountNameHandler(), tenant.ForbiddenAnnotationsRegexHandler(), tenant.ProtectedHandler()),
|
||||
route.OwnerReference(utils.InCapsuleGroups(cfg, ownerreference.Handler(cfg))),
|
||||
route.OwnerReference(utils.InCapsuleGroups(cfg, namespacewebhook.OwnerReferenceHandler(), ownerreference.Handler(cfg))),
|
||||
route.Cordoning(tenant.CordoningHandler(cfg), tenant.ResourceCounterHandler()),
|
||||
route.Node(utils.InCapsuleGroups(cfg, node.UserMetadataHandler(cfg, kubeVersion))),
|
||||
)
|
||||
|
||||
35
pkg/utils/node_selector.go
Normal file
35
pkg/utils/node_selector.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
NodeSelectorAnnotation = "scheduler.alpha.kubernetes.io/node-selector"
|
||||
)
|
||||
|
||||
func BuildNodeSelector(tnt *capsulev1beta1.Tenant, nsAnnotations map[string]string) map[string]string {
|
||||
if nsAnnotations == nil {
|
||||
nsAnnotations = make(map[string]string)
|
||||
}
|
||||
|
||||
selector := make([]string, 0, len(tnt.Spec.NodeSelector))
|
||||
|
||||
for k, v := range tnt.Spec.NodeSelector {
|
||||
selector = append(selector, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
// Sorting the resulting slice: iterating over maps is randomized, and we could end-up
|
||||
// in multiple reconciliations upon multiple node selectors.
|
||||
sort.Strings(selector)
|
||||
|
||||
nsAnnotations[NodeSelectorAnnotation] = strings.Join(selector, ",")
|
||||
|
||||
return nsAnnotations
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func (r *freezedHandler) OnDelete(c client.Client, _ *admission.Decoder, recorde
|
||||
|
||||
tnt := tntList.Items[0]
|
||||
|
||||
if tnt.IsCordoned() && utils.IsCapsuleUser(req, r.configuration.UserGroups()) {
|
||||
if tnt.IsCordoned() && utils.IsCapsuleUser(ctx, req, c, r.configuration.UserGroups()) {
|
||||
recorder.Eventf(&tnt, corev1.EventTypeWarning, "TenantFreezed", "Namespace %s cannot be deleted, the current Tenant is freezed", req.Name)
|
||||
|
||||
response := admission.Denied("the selected Tenant is freezed")
|
||||
@@ -101,7 +101,7 @@ func (r *freezedHandler) OnUpdate(c client.Client, decoder *admission.Decoder, r
|
||||
|
||||
tnt := tntList.Items[0]
|
||||
|
||||
if tnt.IsCordoned() && utils.IsCapsuleUser(req, r.configuration.UserGroups()) {
|
||||
if tnt.IsCordoned() && utils.IsCapsuleUser(ctx, req, c, r.configuration.UserGroups()) {
|
||||
recorder.Eventf(&tnt, corev1.EventTypeWarning, "TenantFreezed", "Namespace %s cannot be updated, the current Tenant is freezed", ns.GetName())
|
||||
|
||||
response := admission.Denied("the selected Tenant is freezed")
|
||||
|
||||
64
pkg/webhook/namespace/owner_reference.go
Normal file
64
pkg/webhook/namespace/owner_reference.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package namespace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
capsulewebhook "github.com/clastix/capsule/pkg/webhook"
|
||||
"github.com/clastix/capsule/pkg/webhook/utils"
|
||||
)
|
||||
|
||||
type ownerReferenceHandler struct{}
|
||||
|
||||
func OwnerReferenceHandler() capsulewebhook.Handler {
|
||||
return &ownerReferenceHandler{}
|
||||
}
|
||||
|
||||
func (r *ownerReferenceHandler) OnCreate(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ownerReferenceHandler) OnDelete(client.Client, *admission.Decoder, record.EventRecorder) capsulewebhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ownerReferenceHandler) OnUpdate(_ client.Client, decoder *admission.Decoder, _ record.EventRecorder) capsulewebhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
oldNs := &corev1.Namespace{}
|
||||
if err := decoder.DecodeRaw(req.OldObject, oldNs); err != nil {
|
||||
return utils.ErroredResponse(err)
|
||||
}
|
||||
|
||||
newNs := &corev1.Namespace{}
|
||||
if err := decoder.Decode(req, newNs); err != nil {
|
||||
return utils.ErroredResponse(err)
|
||||
}
|
||||
|
||||
if len(newNs.OwnerReferences) == 0 {
|
||||
response := admission.Errored(http.StatusBadRequest, fmt.Errorf("the OwnerReference cannot be removed"))
|
||||
|
||||
return &response
|
||||
}
|
||||
|
||||
if oldNs.GetOwnerReferences()[0].UID != newNs.GetOwnerReferences()[0].UID {
|
||||
response := admission.Errored(http.StatusBadRequest, fmt.Errorf("the OwnerReference cannot be changed"))
|
||||
|
||||
return &response
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package namespace
|
||||
|
||||
import (
|
||||
|
||||
@@ -112,6 +112,25 @@ func (r *userMetadataHandler) OnUpdate(client client.Client, decoder *admission.
|
||||
}
|
||||
}
|
||||
|
||||
if len(tnt.Spec.NodeSelector) > 0 {
|
||||
v, ok := newNs.GetAnnotations()["scheduler.alpha.kubernetes.io/node-selector"]
|
||||
if !ok {
|
||||
response := admission.Denied("the node-selector annotation is enforced, cannot be removed")
|
||||
|
||||
recorder.Eventf(tnt, corev1.EventTypeWarning, "ForbiddenNodeSelectorDeletion", string(response.Result.Reason))
|
||||
|
||||
return &response
|
||||
}
|
||||
|
||||
if v != oldNs.GetAnnotations()["scheduler.alpha.kubernetes.io/node-selector"] {
|
||||
response := admission.Denied("the the node-selector annotation is enforced, cannot be updated")
|
||||
|
||||
recorder.Eventf(tnt, corev1.EventTypeWarning, "ForbiddenNodeSelectorUpdate", string(response.Result.Reason))
|
||||
|
||||
return &response
|
||||
}
|
||||
}
|
||||
|
||||
var labels, annotations map[string]string
|
||||
|
||||
for key, value := range newNs.GetLabels() {
|
||||
|
||||
@@ -49,7 +49,7 @@ func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder, rec
|
||||
|
||||
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) capsulewebhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
return h.setOwnerRef(ctx, req, client, decoder, recorder)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ func (h *cordoningHandler) cordonHandler(ctx context.Context, clt client.Client,
|
||||
}
|
||||
|
||||
tnt := tntList.Items[0]
|
||||
if tnt.IsCordoned() && utils.IsCapsuleUser(req, h.configuration.UserGroups()) {
|
||||
if tnt.IsCordoned() && utils.IsCapsuleUser(ctx, req, clt, h.configuration.UserGroups()) {
|
||||
recorder.Eventf(&tnt, corev1.EventTypeWarning, "TenantFreezed", "%s %s/%s cannot be %sd, current Tenant is freezed", req.Kind.String(), req.Namespace, req.Name, strings.ToLower(string(req.Operation)))
|
||||
|
||||
response := admission.Denied(fmt.Sprintf("tenant %s is freezed: please, reach out to the system administrator", tnt.GetName()))
|
||||
|
||||
@@ -28,7 +28,7 @@ type handler struct {
|
||||
|
||||
func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) webhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
if !IsCapsuleUser(req, h.configuration.UserGroups()) {
|
||||
if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (h *handler) OnCreate(client client.Client, decoder *admission.Decoder, rec
|
||||
|
||||
func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) webhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
if !IsCapsuleUser(req, h.configuration.UserGroups()) {
|
||||
if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func (h *handler) OnDelete(client client.Client, decoder *admission.Decoder, rec
|
||||
|
||||
func (h *handler) OnUpdate(client client.Client, decoder *admission.Decoder, recorder record.EventRecorder) webhook.Func {
|
||||
return func(ctx context.Context, req admission.Request) *admission.Response {
|
||||
if !IsCapsuleUser(req, h.configuration.UserGroups()) {
|
||||
if !IsCapsuleUser(ctx, req, client, h.configuration.UserGroups()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
"github.com/clastix/capsule/pkg/utils"
|
||||
)
|
||||
|
||||
func IsCapsuleUser(req admission.Request, userGroups []string) bool {
|
||||
func IsCapsuleUser(ctx context.Context, req admission.Request, clt client.Client, userGroups []string) bool {
|
||||
groupList := utils.NewUserGroupList(req.UserInfo.Groups)
|
||||
// if the user is a ServiceAccount belonging to the kube-system namespace, definitely, it's not a Capsule user
|
||||
// and we can skip the check in case of Capsule user group assigned to system:authenticated
|
||||
@@ -14,6 +21,23 @@ func IsCapsuleUser(req admission.Request, userGroups []string) bool {
|
||||
if groupList.Find("system:serviceaccounts:kube-system") {
|
||||
return false
|
||||
}
|
||||
// nolint:nestif
|
||||
if sets.NewString(req.UserInfo.Groups...).Has("system:serviceaccounts") {
|
||||
parts := strings.Split(req.UserInfo.Username, ":")
|
||||
|
||||
targetNamespace := parts[2]
|
||||
|
||||
if len(targetNamespace) > 0 {
|
||||
tl := &capsulev1beta1.TenantList{}
|
||||
if err := clt.List(ctx, tl, client.MatchingFieldsSelector{Selector: fields.OneTermEqualSelector(".status.namespaces", targetNamespace)}); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(tl.Items) == 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, group := range userGroups {
|
||||
if groupList.Find(group) {
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
## Reference: https://github.com/norwoodj/helm-docs
|
||||
set -eux
|
||||
CHART_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
echo "$CHART_DIR"
|
||||
|
||||
echo "Running Helm-Docs"
|
||||
docker run \
|
||||
-v "$CHART_DIR:/helm-docs" \
|
||||
-u $(id -u) \
|
||||
jnorwood/helm-docs:latest
|
||||
Reference in New Issue
Block a user