diff --git a/charts/devtron-backups/templates/backup-on-azure.yaml b/charts/devtron-backups/templates/backup-on-azure.yaml index 7bb2cbbc..8099e644 100644 --- a/charts/devtron-backups/templates/backup-on-azure.yaml +++ b/charts/devtron-backups/templates/backup-on-azure.yaml @@ -33,24 +33,26 @@ spec: - name: postgres-app-backup-cronjob image: {{ .Values.postgres_backup.postgresImage | default "postgres:12" }} volumeMounts: - - mountPath: /tmp + - mountPath: /postgres name: psql-volume env: - name: PGPASSWORD valueFrom: secretKeyRef: key: PG_PASSWORD - name: {{ .Values.postgres_backup.secretName }} + name: {{ .Values.postgres_backup.secretName }} imagePullPolicy: Always args: - /bin/bash - -c - - pg_dumpall -h {{ .Values.postgres_backup.host | default "postgresql-postgresql.devtroncd" }} --exclude-database=clairv4 -p 5432 -U postgres --no-privileges > /tmp/backup.tar; + - set -ex ; pg_dumpall -h {{ .Values.postgres_backup.host | default "postgresql-postgresql.devtroncd" }} --exclude-database=clairv4 -p 5432 -U postgres --no-privileges > /postgres/backup.tar; echo $? ; du -sh /postgres/backup.tar + resources: +{{ toYaml .Values.postgres_backup.resources | indent 13 }} containers: - name: az-cli-for-upload image: {{ .Values.global.AZURE.image | default "quay.io/devtron/k8s-utils:az-cli-ubuntu" }} volumeMounts: - - mountPath: /tmp + - mountPath: /postgres name: psql-volume env: - name: AZURE_BLOB_ACCOUNT_NAME @@ -74,13 +76,15 @@ spec: name: devtron-azure-backup-secret key: AZURE_BLOB_CONTAINER_FOR_ARGOCD imagePullPolicy: Always + resources: +{{ toYaml .Values.postgres_backup.resources | indent 13 }} args: - /bin/bash - -c - {{- if $.Values.global.AZURE.encryption.enabled }} - - date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.AZURE.encryption.passphrase }} /tmp/backup.tar; rm -rvf /tmp/backup.tar; mv /tmp/backup.tar.gpg /tmp/backup-$date1.tar.gpg; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_POSTGRES --file /tmp/backup-$date1.tar.gpg --account-key $AZURE_ACCOUNT_KEY; + {{- if $.Values.global.AZURE.encryption.enabled }} + - set -ex; date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.AZURE.encryption.passphrase }} /postgres/backup.tar; rm -rvf /postgres/backup.tar; mv /postgres/backup.tar.gpg /postgres/backup-$date1.tar.gpg; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_POSTGRES --file /postgres/backup-$date1.tar.gpg --account-key $AZURE_ACCOUNT_KEY; {{- else }} - - date1=$(date +%Y%m%d-%H%M); mv /tmp/backup.tar /tmp/backup-$date1.tar; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_POSTGRES --file /tmp/backup-$date1.tar --account-key $AZURE_ACCOUNT_KEY; + - set -ex; date1=$(date +%Y%m%d-%H%M); mv /postgres/backup.tar /postgres/backup-$date1.tar; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_POSTGRES --file /postgres/backup-$date1.tar --account-key $AZURE_ACCOUNT_KEY; {{- end }} volumes: - name: psql-volume @@ -96,8 +100,8 @@ apiVersion: batch/v1beta1 {{ end -}} kind: CronJob metadata: - name: argocd-app-backup-cronjob - namespace: devtroncd + name: argocd-app-backup-cronjob + namespace: devtroncd spec: schedule: {{ .Values.global.schedule }} jobTemplate: @@ -108,19 +112,21 @@ spec: - name: argocd-app-backup-job image: {{ .Values.argocd_backup.argocdImage | default "quay.io/argoproj/argocd:v2.4.0" }} volumeMounts: - - mountPath: /cache + - mountPath: /argocd name: argocd-volume env: imagePullPolicy: Always args: - /bin/bash - -c - - {{ .Values.argocd_backup.args | default "argocd admin export -n devtroncd > /cache/backup.yaml" }} + - {{ .Values.argocd_backup.args | default "argocd admin export -n devtroncd > /argocd/backup.yaml" }} + resources: +{{ toYaml .Values.argocd_backup.resources | indent 13 }} containers: - name: az-cli-for-upload image: {{ .Values.global.AZURE.image | default "quay.io/devtron/k8s-utils:az-cli-ubuntu" }} volumeMounts: - - mountPath: /cache + - mountPath: /argocd name: argocd-volume env: - name: AZURE_BLOB_ACCOUNT_NAME @@ -144,13 +150,15 @@ spec: name: devtron-azure-backup-secret key: AZURE_BLOB_CONTAINER_FOR_ARGOCD imagePullPolicy: Always + resources: +{{ toYaml .Values.argocd_backup.resources | indent 13 }} args: - /bin/bash - -c {{- if $.Values.global.AZURE.encryption.enabled }} - - date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.AZURE.encryption.passphrase }} /cache/backup.yaml ;rm -rvf /cache/backup.yaml ;mv /cache/backup.yaml.gpg /cache/backup-$date1.yaml.gpg; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_ARGOCD --file /cache/backup-$date1.yaml.gpg --account-key $AZURE_ACCOUNT_KEY; + - set -ex; date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.AZURE.encryption.passphrase }} /argocd/backup.yaml ;rm -rvf /argocd/backup.yaml ;mv /argocd/backup.yaml.gpg /argocd/backup-$date1.yaml.gpg; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_ARGOCD --file /argocd/backup-$date1.yaml.gpg --account-key $AZURE_ACCOUNT_KEY; {{- else }} - - date1=$(date +%Y%m%d-%H%M); mv /cache/backup.yaml /cache/backup-$date1.yaml; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_ARGOCD --file /cache/backup-$date1.yaml --account-key $AZURE_ACCOUNT_KEY; + - set -ex; date1=$(date +%Y%m%d-%H%M); mv /argocd/backup.yaml /argocd/backup-$date1.yaml; az storage blob upload --account-name $AZURE_BLOB_ACCOUNT_NAME --container-name $AZURE_BLOB_CONTAINER_FOR_ARGOCD --file /argocd/backup-$date1.yaml --account-key $AZURE_ACCOUNT_KEY; {{- end }} volumes: - name: argocd-volume @@ -158,4 +166,4 @@ spec: restartPolicy: OnFailure serviceAccountName: {{ .Values.argocd_backup.serviceAccountName }} {{- end }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/devtron-backups/templates/backup-on-gcp.yaml b/charts/devtron-backups/templates/backup-on-gcp.yaml index 7dd5b410..513b2653 100644 --- a/charts/devtron-backups/templates/backup-on-gcp.yaml +++ b/charts/devtron-backups/templates/backup-on-gcp.yaml @@ -33,7 +33,7 @@ spec: - name: postgres-app-backup-cronjob image: {{ .Values.postgres_backup.postgresImage | default "postgres:12" }} volumeMounts: - - mountPath: /tmp + - mountPath: /postgres name: psql-volume env: - name: PGPASSWORD @@ -45,12 +45,14 @@ spec: args: - /bin/bash - -c - - pg_dumpall -h {{ .Values.postgres_backup.host | default "postgresql-postgresql.devtroncd" }} --exclude-database=clairv4 -p 5432 -U postgres --no-privileges > /tmp/backup.tar; + - set -ex; pg_dumpall -h {{ .Values.postgres_backup.host | default "postgresql-postgresql.devtroncd" }} --exclude-database=clairv4 -p 5432 -U postgres --no-privileges > /postgres/backup.tar; echo $? ; du -sh /postgres/backup.tar + resources: +{{ toYaml .Values.postgres_backup.resources | indent 13 }} containers: - name: gcloud-cli-for-cloud-storage-upload image: {{ .Values.global.GCP.image | default "google/cloud-sdk:alpine" }} volumeMounts: - - mountPath: /tmp + - mountPath: /postgres name: psql-volume env: - name: GCS_BUCKET @@ -66,13 +68,15 @@ spec: key: GOOGLE_APPLICATION_CREDENTIALS {{- end }} imagePullPolicy: Always + resources: +{{ toYaml .Values.postgres_backup.resources | indent 13 }} args: - /bin/bash - -c {{- if $.Values.global.GCP.encryption.enabled }} - - echo $gcp_credentials > /tmp/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/tmp/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.GCP.encryption.passphrase }} /tmp/backup.tar; rm -rvf /tmp/backup.tar; mv /tmp/backup.tar.gpg /tmp/backup-$date1.tar.gpg; gsutil cp /tmp/backup-$date1.tar.gpg gs://$GCS_BUCKET/postgres/; + - set -ex; echo $gcp_credentials > /postgres/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/postgres/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.GCP.encryption.passphrase }} /postgres/backup.tar; rm -rvf /postgres/backup.tar; mv /postgres/backup.tar.gpg /postgres/backup-$date1.tar.gpg; gsutil cp /postgres/backup-$date1.tar.gpg gs://$GCS_BUCKET/postgres/; {{- else}} - - echo $gcp_credentials > /tmp/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/tmp/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); mv /tmp/backup.tar /tmp/backup-$date1.tar; gsutil cp /tmp/backup-$date1.tar gs://$GCS_BUCKET/postgres/; + - set -ex; echo $gcp_credentials > /postgres/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/postgres/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); mv /postgres/backup.tar /postgres/backup-$date1.tar; gsutil cp /postgres/backup-$date1.tar gs://$GCS_BUCKET/postgres/; {{- end }} volumes: - name: psql-volume @@ -101,19 +105,21 @@ spec: - name: argocd-app-backup-job image: {{ .Values.argocd_backup.argocdImage | default "quay.io/argoproj/argocd:v2.4.0" }} volumeMounts: - - mountPath: /cache + - mountPath: /argocd name: argocd-volume env: imagePullPolicy: Always args: - /bin/bash - -c - - {{ .Values.argocd_backup.args | default "argocd admin export -n devtroncd > /cache/backup.yaml" }} + - {{ .Values.argocd_backup.args | default "argocd admin export -n devtroncd > /argocd/backup.yaml" }} + resources: +{{ toYaml .Values.argocd_backup.resources | indent 13 }} containers: - name: gcloud-cli-for-cloud-storage-upload image: {{ .Values.global.GCP.image | default "google/cloud-sdk:alpine" }} volumeMounts: - - mountPath: /cache + - mountPath: /argocd name: argocd-volume env: - name: GCS_BUCKET @@ -129,13 +135,15 @@ spec: key: GOOGLE_APPLICATION_CREDENTIALS {{- end }} imagePullPolicy: Always + resources: +{{ toYaml .Values.argocd_backup.resources | indent 13 }} args: - /bin/bash - -c {{- if $.Values.global.GCP.encryption.enabled }} - - echo $gcp_credentials > /tmp/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/tmp/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.GCP.encryption.passphrase }} /cache/backup.yaml; rm -rvf /cache/backup.yaml; mv /cache/backup.yaml.gpg /cache/backup-$date1.yaml.gpg; gsutil cp /cache/backup-$date1.yaml.gpg gs://$GCS_BUCKET/argocd/; + - set -ex; echo $gcp_credentials > /argocd/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/argocd/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); gpg -c --batch --passphrase {{ .Values.global.GCP.encryption.passphrase }} /argocd/backup.yaml; rm -rvf /argocd/backup.yaml; mv /argocd/backup.yaml.gpg /argocd/backup-$date1.yaml.gpg; gsutil cp /argocd/backup-$date1.yaml.gpg gs://$GCS_BUCKET/argocd/; {{- else}} - - echo $gcp_credentials > /tmp/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/tmp/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); mv /cache/backup.yaml /cache/backup-$date1.yaml; gsutil cp /cache/backup-$date1.yaml gs://$GCS_BUCKET/argocd/; + - set -ex; echo $gcp_credentials > /argocd/gcp_credentials.json ; gcloud auth activate-service-account --key-file=/argocd/gcp_credentials.json ; date1=$(date +%Y%m%d-%H%M); mv /argocd/backup.yaml /argocd/backup-$date1.yaml; gsutil cp /argocd/backup-$date1.yaml gs://$GCS_BUCKET/argocd/; {{- end }} volumes: - name: argocd-volume diff --git a/charts/k8s-shield/.helmignore b/charts/k8s-shield/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/charts/k8s-shield/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/k8s-shield/Chart.yaml b/charts/k8s-shield/Chart.yaml new file mode 100644 index 00000000..1d37c001 --- /dev/null +++ b/charts/k8s-shield/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +appVersion: 1.0.0 +description: A Helm chart for Kubernetes admission policies +name: k8s-shield +type: application +version: 0.1.0 +maintainers: +- email: devops@devtron.ai + name: Devtron Devops Team +- email: neha.sharma@devtron.ai + name: Neha Sharma diff --git a/charts/k8s-shield/templates/_helpers.tpl b/charts/k8s-shield/templates/_helpers.tpl new file mode 100644 index 00000000..fd2ec1af --- /dev/null +++ b/charts/k8s-shield/templates/_helpers.tpl @@ -0,0 +1,16 @@ +{{- define "k8s-shield.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "k8s-shield.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/k8s-shield/templates/container-security-context-policy.yaml b/charts/k8s-shield/templates/container-security-context-policy.yaml new file mode 100644 index 00000000..28cf3cb4 --- /dev/null +++ b/charts/k8s-shield/templates/container-security-context-policy.yaml @@ -0,0 +1,68 @@ +{{- if .Values.containerSecurityPolicy.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: "{{ include "k8s-shield.fullname" . }}-container-security" +spec: + failurePolicy: Fail + matchConstraints: + resourceRules: + - apiGroups: + {{- range $.Values.resourcePolicies.WithoutResource.apiGroups }} + - {{ . | quote }} + {{- end }} + apiVersions: + - v1 + operations: + {{- range $.Values.resourcePolicies.WithoutResource.operations }} + - {{ . | quote }} + {{- end }} + resources: + {{- range $.Values.resourcePolicies.WithoutResource.resources }} + - {{ . | quote }} + {{- end }} + scope: '*' + validations: + - expression: "object.kind != 'Pod' || has(object.metadata.labels) && object.metadata.labels['business-unit'] == 'finance'" + message: "Pods must have a 'business-unit: finance' label" + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.runAsNonRoot) && c.securityContext.runAsNonRoot == {{ .Values.containerSecurityPolicy.allowrunAsNonRoot }})" + message: "All containers must set runAsNonRoot to {{ .Values.containerSecurityPolicy.allowrunAsNonRoot }}" + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.readOnlyRootFilesystem) && c.securityContext.readOnlyRootFilesystem == {{ .Values.containerSecurityPolicy.allowreadOnlyRootFilesystem }})" + message: "All containers must set readOnlyRootFilesystem to {{ .Values.containerSecurityPolicy.allowreadOnlyRootFilesystem }}" + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.allowPrivilegeEscalation) || c.securityContext.allowPrivilegeEscalation == {{ .Values.containerSecurityPolicy.allowPrivilegeEscalation }})" + message: "All containers must set allowPrivilegeEscalation to {{ .Values.containerSecurityPolicy.allowPrivilegeEscalation }}" + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged == {{ .Values.containerSecurityPolicy.allowprivileged }})" + message: "All containers must set privileged to {{ .Values.containerSecurityPolicy.allowprivileged }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || has(object.metadata.labels) && object.metadata.labels['business-unit'] == 'finance'" + message: "Workload resources must have a 'business-unit: finance' label" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.runAsNonRoot) && c.securityContext.runAsNonRoot == {{ .Values.containerSecurityPolicy.allowrunAsNonRoot }})" + message: "All workload containers must set runAsNonRoot to {{ .Values.containerSecurityPolicy.allowrunAsNonRoot }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.readOnlyRootFilesystem) && c.securityContext.readOnlyRootFilesystem == {{ .Values.containerSecurityPolicy.allowreadOnlyRootFilesystem }})" + message: "All workload containers must set readOnlyRootFilesystem to {{ .Values.containerSecurityPolicy.allowreadOnlyRootFilesystem }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.allowPrivilegeEscalation) || c.securityContext.allowPrivilegeEscalation == {{ .Values.containerSecurityPolicy.allowPrivilegeEscalation }})" + message: "All workload containers must set allowPrivilegeEscalation to {{ .Values.containerSecurityPolicy.allowPrivilegeEscalation }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged == {{ .Values.containerSecurityPolicy.allowprivileged }})" + message: "All workload containers must set privileged to {{ .Values.containerSecurityPolicy.allowprivileged }}" + - expression: "object.kind != 'CronJob' || has(object.metadata.labels) && object.metadata.labels['business-unit'] == 'finance'" + message: "CronJobs must have a 'business-unit: finance' label" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.runAsNonRoot) && c.securityContext.runAsNonRoot == {{ .Values.containerSecurityPolicy.allowrunAsNonRoot }})" + message: "All CronJob containers must set runAsNonRoot to {{ .Values.containerSecurityPolicy.allowrunAsNonRoot }}" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.readOnlyRootFilesystem) && c.securityContext.readOnlyRootFilesystem == {{ .Values.containerSecurityPolicy.allowreadOnlyRootFilesystem }})" + message: "All CronJob containers must set readOnlyRootFilesystem to {{ .Values.containerSecurityPolicy.allowreadOnlyRootFilesystem }}" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.allowPrivilegeEscalation) || c.securityContext.allowPrivilegeEscalation == {{ .Values.containerSecurityPolicy.allowPrivilegeEscalation }})" + message: "All CronJob containers must set allowPrivilegeEscalation to {{ .Values.containerSecurityPolicy.allowPrivilegeEscalation }}" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged == {{ .Values.containerSecurityPolicy.allowprivileged }})" + message: "All CronJob containers must set privileged to {{ .Values.containerSecurityPolicy.allowprivileged }}" +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-container-security-binding +spec: + policyName: {{ include "k8s-shield.fullname" . }}-container-security + validationActions: + {{- range .Values.containerSecurityPolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} + diff --git a/charts/k8s-shield/templates/pod-security-context-policy.yaml b/charts/k8s-shield/templates/pod-security-context-policy.yaml new file mode 100644 index 00000000..89261d09 --- /dev/null +++ b/charts/k8s-shield/templates/pod-security-context-policy.yaml @@ -0,0 +1,61 @@ +{{- if .Values.podSecurityPolicy.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: "{{ include "k8s-shield.fullname" . }}-pod-security" +spec: + failurePolicy: Fail + matchConstraints: + resourceRules: + - apiGroups: + {{- range $.Values.resourcePolicies.WithoutResource.apiGroups }} + - {{ . | quote }} + {{- end }} + apiVersions: + - v1 + operations: + {{- range $.Values.resourcePolicies.WithoutResource.operations }} + - {{ . | quote }} + {{- end }} + resources: + {{- range $.Values.resourcePolicies.WithoutResource.resources }} + - {{ . | quote }} + {{- end }} + scope: '*' + validations: + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.runAsNonRoot) && c.securityContext.runAsNonRoot == {{ .Values.podSecurityPolicy.allowrunAsNonRoot }})" + message: "All containers must set runAsNonRoot to {{ .Values.podSecurityPolicy.allowrunAsNonRoot }}" + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.readOnlyRootFilesystem) && c.securityContext.readOnlyRootFilesystem == {{ .Values.podSecurityPolicy.allowreadOnlyRootFilesystem }})" + message: "All containers must set readOnlyRootFilesystem to {{ .Values.podSecurityPolicy.allowreadOnlyRootFilesystem }}" + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.allowPrivilegeEscalation) || c.securityContext.allowPrivilegeEscalation == {{ .Values.podSecurityPolicy.allowPrivilegeEscalation }})" + message: "All containers must set allowPrivilegeEscalation to {{ .Values.podSecurityPolicy.allowPrivilegeEscalation }}" + - expression: "object.kind != 'Pod' || object.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged == {{ .Values.podSecurityPolicy.allowprivileged }})" + message: "All containers must set privileged to {{ .Values.podSecurityPolicy.allowprivileged }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.runAsNonRoot) && c.securityContext.runAsNonRoot == {{ .Values.podSecurityPolicy.allowrunAsNonRoot }})" + message: "All workload containers must set runAsNonRoot to {{ .Values.podSecurityPolicy.allowrunAsNonRoot }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.readOnlyRootFilesystem) && c.securityContext.readOnlyRootFilesystem == {{ .Values.podSecurityPolicy.allowreadOnlyRootFilesystem }})" + message: "All workload containers must set readOnlyRootFilesystem to {{ .Values.podSecurityPolicy.allowreadOnlyRootFilesystem }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.allowPrivilegeEscalation) || c.securityContext.allowPrivilegeEscalation == {{ .Values.podSecurityPolicy.allowPrivilegeEscalation }})" + message: "All workload containers must set allowPrivilegeEscalation to {{ .Values.podSecurityPolicy.allowPrivilegeEscalation }}" + - expression: "['Deployment', 'ReplicaSet', 'DaemonSet', 'StatefulSet', 'Job'].all(kind, object.kind != kind) || object.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged == {{ .Values.podSecurityPolicy.allowprivileged }})" + message: "All workload containers must set privileged to {{ .Values.podSecurityPolicy.allowprivileged }}" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.runAsNonRoot) && c.securityContext.runAsNonRoot == {{ .Values.podSecurityPolicy.allowrunAsNonRoot }})" + message: "All CronJob containers must set runAsNonRoot to {{ .Values.podSecurityPolicy.allowrunAsNonRoot }}" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, has(c.securityContext) && has(c.securityContext.readOnlyRootFilesystem) && c.securityContext.readOnlyRootFilesystem == {{ .Values.podSecurityPolicy.allowreadOnlyRootFilesystem }})" + message: "All CronJob containers must set readOnlyRootFilesystem to {{ .Values.podSecurityPolicy.allowreadOnlyRootFilesystem }}" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.allowPrivilegeEscalation) || c.securityContext.allowPrivilegeEscalation == {{ .Values.podSecurityPolicy.allowPrivilegeEscalation }})" + message: "All CronJob containers must set allowPrivilegeEscalation to {{ .Values.podSecurityPolicy.allowPrivilegeEscalation }}" + - expression: "object.kind != 'CronJob' || object.spec.jobTemplate.spec.template.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged == {{ .Values.podSecurityPolicy.allowprivileged }})" + message: "All CronJob containers must set privileged to {{ .Values.podSecurityPolicy.allowprivileged }}" +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-pod-security-binding +spec: + policyName: {{ include "k8s-shield.fullname" . }}-pod-security + validationActions: + {{- range .Values.podSecurityPolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/charts/k8s-shield/templates/restrict-admin-cluster-role-creation-policy.yaml b/charts/k8s-shield/templates/restrict-admin-cluster-role-creation-policy.yaml new file mode 100644 index 00000000..ac35bba7 --- /dev/null +++ b/charts/k8s-shield/templates/restrict-admin-cluster-role-creation-policy.yaml @@ -0,0 +1,58 @@ +{{- if $.Values.adminClusterRoleCreation.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-deny-admin-cluster-role-creation +spec: + failurePolicy: Fail + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + resourceRules: + - apiGroups: + - rbac.authorization.k8s.io + apiVersions: + - "*" + operations: + - CREATE + - UPDATE + resources: + - clusterroles + scope: '*' + validations: + {{- if and .Values.bypassLabel.key .Values.bypassLabel.value }} + - expression: "(has(object.metadata.labels) && object.metadata.labels.exists(l, l == '{{ .Values.bypassLabel.key }}') && object.metadata.labels['{{ .Values.bypassLabel.key }}'] == '{{ .Values.bypassLabel.value }}') || !has(object.rules) || object.rules.all(rule, + !(rule.apiGroups.exists(g, g == '*') && + rule.resources.exists(r, r == '*') && + rule.verbs.exists(v, v == '*')) && + !(rule.nonResourceURLs.exists(u, u == '*') && + rule.verbs.exists(v, v == '*')) + )" + message: "Creation of ClusterRole with admin access is denied, set label {{ .Values.bypassLabel.key }}: {{ .Values.bypassLabel.value }} to allow." + {{- else }} + - expression: "!has(object.rules) || object.rules.all(rule, + !(rule.apiGroups.exists(g, g == '*') && + rule.resources.exists(r, r == '*') && + rule.verbs.exists(v, v == '*')) && + !(rule.nonResourceURLs.exists(u, u == '*') && + rule.verbs.exists(v, v == '*'))" + message: "Creation of ClusterRole with admin access is denied" + {{- end }} + +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-deny-admin-cluster-role-creation-binding +spec: + matchResources: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + policyName: {{ include "k8s-shield.fullname" . }}-deny-admin-cluster-role-creation + validationActions: + {{- range .Values.adminClusterRoleCreation.validationActions }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/charts/k8s-shield/templates/restrict-application-deletion-policy.yaml b/charts/k8s-shield/templates/restrict-application-deletion-policy.yaml new file mode 100644 index 00000000..e702cc4f --- /dev/null +++ b/charts/k8s-shield/templates/restrict-application-deletion-policy.yaml @@ -0,0 +1,51 @@ +{{- if $.Values.appDeletionPolicy.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-prevent-app-deletion +spec: + failurePolicy: Fail + {{- if and .Values.appDeletionPolicy.namespaces }} + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- range .Values.appDeletionPolicy.namespaces }} + - {{ . | quote }} + {{- end }} + {{- else }} + matchConstraints: {} + {{- end }} + objectSelector: {} + resourceRules: + - apiGroups: + - argoproj.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - applications + scope: Namespaced + validations: + - expression: "false" + message: Deletion of application is not allowed. +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-prevent-app-deletion-binding +spec: + matchResources: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + policyName: {{ include "k8s-shield.fullname" . }}-prevent-app-deletion + validationActions: + {{- range .Values.appDeletionPolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/k8s-shield/templates/restrict-cluster-rolebinding-with-cluster-admin-creation-policy.yaml b/charts/k8s-shield/templates/restrict-cluster-rolebinding-with-cluster-admin-creation-policy.yaml new file mode 100644 index 00000000..385553c4 --- /dev/null +++ b/charts/k8s-shield/templates/restrict-cluster-rolebinding-with-cluster-admin-creation-policy.yaml @@ -0,0 +1,42 @@ +{{- if $.Values.cLusterRoleBindingCreation.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-cluster-role-binding-creation +spec: + failurePolicy: Fail + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + resourceRules: + - apiGroups: + - rbac.authorization.k8s.io + apiVersions: + - "*" + operations: + - CREATE + - UPDATE + resources: + - clusterrolebindings + scope: '*' + validations: + {{- if and .Values.bypassLabel.key .Values.bypassLabel.value }} + - expression: "(has(object.metadata.labels) && object.metadata.labels.exists(l, l == '{{ .Values.bypassLabel.key }}') && object.metadata.labels['{{ .Values.bypassLabel.key }}'] == '{{ .Values.bypassLabel.value }}') || !(object.roleRef.apiGroup == 'rbac.authorization.k8s.io' && object.roleRef.kind == 'ClusterRole' && object.roleRef.name == 'cluster-admin')" + message: "Binding to the cluster-admin ClusterRole is bypassed by security policy, set label {{ .Values.bypassLabel.key }}: {{ .Values.bypassLabel.value }} allow" + {{- else }} + - expression: "!(object.roleRef.apiGroup == 'rbac.authorization.k8s.io' && object.roleRef.kind == 'ClusterRole' && object.roleRef.name == 'cluster-admin')" + message: "Binding to the cluster-admin ClusterRole is not allowed" + {{- end }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-cluster-role-binding-creation-binding +spec: + policyName: {{ include "k8s-shield.fullname" . }}-restrict-cluster-role-binding-creation + validationActions: + {{- range .Values.cLusterRoleBindingCreation.validationActions }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/charts/k8s-shield/templates/restrict-limit-resource-policy.yaml b/charts/k8s-shield/templates/restrict-limit-resource-policy.yaml new file mode 100644 index 00000000..578e44b7 --- /dev/null +++ b/charts/k8s-shield/templates/restrict-limit-resource-policy.yaml @@ -0,0 +1,97 @@ +{{- if .Values.resourcePolicies.limitResourcePolicy.enabled }} +{{- $maxCPU := .Values.resourcePolicies.limitResourcePolicy.maxCPULimit | default "1000m" }} +{{- $maxMemory := .Values.resourcePolicies.limitResourcePolicy.maxMemoryLimit | default "2Gi" }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-resource-limits +spec: + failurePolicy: Fail + {{- if and .Values.resourcePolicies.limitResourcePolicy.namespaces }} + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- range .Values.resourcePolicies.limitResourcePolicy.namespaces }} + - {{ . | quote }} + {{- end }} + {{- else }} + matchConstraints: {} + {{- end }} + objectSelector: {} + resourceRules: + - apiGroups: + {{- range $.Values.resourcePolicies.WithoutResource.apiGroups }} + - {{ . | quote }} + {{- end }} + apiVersions: + - v1 + operations: + {{- range .Values.resourcePolicies.limitResourcePolicy.operations }} + - {{ . | quote }} + {{- end }} + resources: + {{- range .Values.resourcePolicies.limitResourcePolicy.resources }} + - {{ . | quote }} + {{- end }} + scope: Namespaced + validations: + {{- if and .Values.bypassLabel.key .Values.bypassLabel.value }} + - expression: |- + (has(object.metadata.labels) && object.metadata.labels['{{ .Values.bypassLabel.key }}'] == '{{ .Values.bypassLabel.value }}') + || ( + has(object.spec.template) ? + ( + !has(object.spec.template.spec.containers[0].resources.limits) || ( + (!has(object.spec.template.spec.containers[0].resources.limits.cpu) || + quantity(object.spec.template.spec.containers[0].resources.limits.cpu).compareTo(quantity('{{ $maxCPU }}')) <= 0) && + (!has(object.spec.template.spec.containers[0].resources.limits.memory) || + quantity(object.spec.template.spec.containers[0].resources.limits.memory).compareTo(quantity('{{ $maxMemory }}')) <= 0) + ) + ) : + ( + !has(object.spec.containers[0].resources.limits) || ( + (!has(object.spec.containers[0].resources.limits.cpu) || + quantity(object.spec.containers[0].resources.limits.cpu).compareTo(quantity('{{ $maxCPU }}')) <= 0) && + (!has(object.spec.containers[0].resources.limits.memory) || + quantity(object.spec.containers[0].resources.limits.memory).compareTo(quantity('{{ $maxMemory }}')) <= 0) + ) + ) + ) + message: "Resource limits exceed the maximum allowed: CPU <= {{ $maxCPU }} and memory <= {{ $maxMemory }}, set label {{ .Values.bypassLabel.key }}: {{ .Values.bypassLabel.value }} to allow" + {{- else }} + - expression: |- + has(object.spec.template) ? + ( + !has(object.spec.template.spec.containers[0].resources.limits) || ( + (!has(object.spec.template.spec.containers[0].resources.limits.cpu) || + quantity(object.spec.template.spec.containers[0].resources.limits.cpu).compareTo(quantity('{{ $maxCPU }}')) <= 0) && + (!has(object.spec.template.spec.containers[0].resources.limits.memory) || + quantity(object.spec.template.spec.containers[0].resources.limits.memory).compareTo(quantity('{{ $maxMemory }}')) <= 0) + ) + ) : + ( + !has(object.spec.containers[0].resources.limits) || ( + (!has(object.spec.containers[0].resources.limits.cpu) || + quantity(object.spec.containers[0].resources.limits.cpu).compareTo(quantity('{{ $maxCPU }}')) <= 0) && + (!has(object.spec.containers[0].resources.limits.memory) || + quantity(object.spec.containers[0].resources.limits.memory).compareTo(quantity('{{ $maxMemory }}')) <= 0) + ) + ) + message: "Resource limits exceed the maximum allowed: CPU <= {{ $maxCPU }} and memory <= {{ $maxMemory }}." + {{- end }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-resource-limits-binding +spec: + policyName: {{ include "k8s-shield.fullname" . }}-restrict-resource-limits + validationActions: + {{- range .Values.resourcePolicies.limitResourcePolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/k8s-shield/templates/restrict-loadbalance-creation-policy.yaml b/charts/k8s-shield/templates/restrict-loadbalance-creation-policy.yaml new file mode 100644 index 00000000..5ab89f03 --- /dev/null +++ b/charts/k8s-shield/templates/restrict-loadbalance-creation-policy.yaml @@ -0,0 +1,57 @@ +{{- if $.Values.loadBalancerCreationPolicy.enabled }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-loadbalancer-creation +spec: + failurePolicy: Fail + {{- if and .Values.loadBalancerCreationPolicy.namespaces }} + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- range .Values.loadBalancerCreationPolicy.namespaces }} + - {{ . | quote }} + {{- end }} + {{- else }} + matchConstraints: {} + {{- end }} + objectSelector: {} + resourceRules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + resources: + - services + scope: Namespaced + validations: + {{- if and .Values.bypassLabel.key .Values.bypassLabel.value }} + - expression: "object.spec.type == 'LoadBalancer' && has(object.metadata.labels) && object.metadata.labels['{{ .Values.bypassLabel.key }}'] == '{{ .Values.bypassLabel.value }}'" + message: "Creation of Services with type LoadBalancer is not allowed, set label {{ .Values.bypassLabel.key }}: {{ .Values.bypassLabel.value }} to allow." + {{- else }} + - expression: "object.spec.type == 'LoadBalancer'" + message: "Creation of Services with type LoadBalancer is not allowed." + {{- end }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-loadbalancer-creation-binding +spec: + matchResources: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + policyName: {{ include "k8s-shield.fullname" . }}-restrict-loadbalancer-creation + validationActions: + {{- range .Values.loadBalancerCreationPolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/charts/k8s-shield/templates/restrict-namespace-deletion-policy.yaml b/charts/k8s-shield/templates/restrict-namespace-deletion-policy.yaml new file mode 100644 index 00000000..3bf3b57c --- /dev/null +++ b/charts/k8s-shield/templates/restrict-namespace-deletion-policy.yaml @@ -0,0 +1,45 @@ +{{- if $.Values.namespaceDeletionPolicy.enabled }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-namespace-deletion +spec: + failurePolicy: Fail + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + resourceRules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - DELETE + resourceNames: + {{- range $.Values.namespaceDeletionPolicy.namespaces }} + - {{ . }} + {{- end }} + resources: + - namespaces + scope: "*" + validations: + - expression: "false" + message: Deletion of namespace is not allowed. +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-namespace-deletion-binding +spec: + matchResources: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + policyName: {{ include "k8s-shield.fullname" . }}-restrict-namespace-deletion + validationActions: + {{- range .Values.namespaceDeletionPolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/charts/k8s-shield/templates/restrict-pvc-creation-policy.yaml b/charts/k8s-shield/templates/restrict-pvc-creation-policy.yaml new file mode 100644 index 00000000..67ee3b3e --- /dev/null +++ b/charts/k8s-shield/templates/restrict-pvc-creation-policy.yaml @@ -0,0 +1,52 @@ +{{- if $.Values.pvcCreationPolicy.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-pvc-creation +spec: + failurePolicy: Fail + {{- if and .Values.pvcCreationPolicy.namespaces }} + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- range .Values.pvcCreationPolicy.namespaces }} + - {{ . | quote }} + {{- end }} + {{- else }} + matchConstraints: {} + {{- end }} + objectSelector: {} + resourceRules: + - apiGroups: [""] + apiVersions: ["v1"] + operations: ["CREATE", "UPDATE"] + scope: Namespaced + resources: ["persistentvolumeclaims"] + validations: + {{- if and .Values.bypassLabel.key .Values.bypassLabel.value }} + - expression: "has(object.metadata.labels) && object.metadata.labels['{{ .Values.bypassLabel.key }}'] == '{{ .Values.bypassLabel.value }}'" + message: "Creation of PersistentVolumeClaims is not allowed, set label {{ .Values.bypassLabel.key }}: {{ .Values.bypassLabel.value }} to allow" + {{- else }} + - expression: "false" + message: "Creation of PersistentVolumeClaims is not allowed." + {{- end }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-pvc-creation-binding +spec: + matchResources: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + policyName: {{ include "k8s-shield.fullname" . }}-restrict-pvc-creation + validationActions: + {{- range .Values.pvcCreationPolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/k8s-shield/templates/restrict-without-readiness-liveness-policy.yaml b/charts/k8s-shield/templates/restrict-without-readiness-liveness-policy.yaml new file mode 100644 index 00000000..fef929d3 --- /dev/null +++ b/charts/k8s-shield/templates/restrict-without-readiness-liveness-policy.yaml @@ -0,0 +1,64 @@ +{{- if $.Values.readinessAndLivenessPolicy.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ .Chart.Name }}-restrict-without-readiness-liveness +spec: + failurePolicy: Fail + {{- if and .Values.readinessAndLivenessPolicy.namespaces }} + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- range $.Values.readinessAndLivenessPolicy.namespaces }} + - {{ . | quote }} + {{- end }} + {{- else }} + matchConstraints: {} + {{- end }} + objectSelector: {} + resourceRules: + - apiGroups: + {{- range $.Values.resourcePolicies.WithoutResource.apiGroups }} + - {{ . | quote }} + {{- end }} + apiVersions: ["v1"] + operations: + {{- range $.Values.readinessAndLivenessPolicy.operations }} + - {{ . | quote }} + {{- end }} + resources: + {{- range $.Values.readinessAndLivenessPolicy.resources }} + - {{ . | quote }} + {{- end }} + scope: Namespaced + validations: + {{- if and .Values.bypassLabel.key .Values.bypassLabel.value }} + - expression: > + (has(object.metadata.labels) && + object.metadata.labels['{{ .Values.bypassLabel.key }}'] == '{{ .Values.bypassLabel.value }}') || + object.spec.template.spec.containers.all(container, + has(container.readinessProbe) && + has(container.livenessProbe) + ) + message: "Deployments without readiness and liveness probes are only allowed for '{{ .Values.bypassLabel.key }}: {{ .Values.bypassLabel.value }}'." + {{- else }} + - expression: object.spec.template.spec.containers.all(container, + has(container.readinessProbe) && has(container.livenessProbe)) + message: "Deployments without readiness and liveness probes are not allowed" + {{- end }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ .Chart.Name }}-restrict-without-readiness-liveness +spec: + policyName: {{ .Chart.Name }}-restrict-without-readiness-liveness + validationActions: + {{- range $.Values.readinessAndLivenessPolicy.validationActions }} + - {{ . }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/k8s-shield/templates/restrict-without-resource-policy.yaml b/charts/k8s-shield/templates/restrict-without-resource-policy.yaml new file mode 100644 index 00000000..96d3ed09 --- /dev/null +++ b/charts/k8s-shield/templates/restrict-without-resource-policy.yaml @@ -0,0 +1,61 @@ +{{- if $.Values.resourcePolicies.WithoutResource.enabled }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicy +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-without-resources +spec: + failurePolicy: Fail + {{- if and .Values.resourcePolicies.WithoutResource.namespaces }} + matchConstraints: + matchPolicy: Equivalent + namespaceSelector: + matchExpressions: + - key: kubernetes.io/metadata.name + operator: In + values: + {{- range $.Values.resourcePolicies.WithoutResource.namespaces }} + - {{ . | quote }} + {{- end }} + {{- else }} + matchConstraints: {} + {{- end }} + resourceRules: + - apiGroups: + {{- range $.Values.resourcePolicies.WithoutResource.apiGroups }} + - {{ . | quote }} + {{- end }} + apiVersions: + - v1 + operations: + {{- range $.Values.resourcePolicies.WithoutResource.operations }} + - {{ . | quote }} + {{- end }} + resources: + {{- range $.Values.resourcePolicies.WithoutResource.resources }} + - {{ . | quote }} + {{- end }} + scope: Namespaced + validations: + {{- if and .Values.bypassLabel.key .Values.bypassLabel.value }} + - expression: "(has(object.metadata.labels) && object.metadata.labels['{{ .Values.bypassLabel.key }}'] == '{{ .Values.bypassLabel.value }}') || object.spec.template.spec.containers.all(c, has(c.resources) && has(c.resources.requests) && has(c.resources.limits) && has(c.resources.requests.cpu) && has(c.resources.requests.memory) && has(c.resources.limits.cpu) && has(c.resources.limits.memory))" + message: "All Containers without CPU and memory's requests and limits are only allowed for '{{ .Values.bypassLabel.key }}: {{ .Values.bypassLabel.value }}'." + {{- else }} + - expression: "object.spec.template.spec.containers.all(c, has(c.resources) && has(c.resources.requests) && has(c.resources.limits) && has(c.resources.requests.cpu) && has(c.resources.requests.memory) && has(c.resources.limits.cpu) && has(c.resources.limits.memory))" + message: "All Containers without CPU and memory's requests and limits are not allowed." + {{- end }} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingAdmissionPolicyBinding +metadata: + name: {{ include "k8s-shield.fullname" . }}-restrict-without-resources-binding +spec: + matchResources: + matchPolicy: Equivalent + namespaceSelector: {} + objectSelector: {} + policyName: {{ include "k8s-shield.fullname" . }}-restrict-without-resources + validationActions: + {{- range $.Values.resourcePolicies.WithoutResource.validationActions }} + - {{ . }} + {{- end }} +{{- end }} diff --git a/charts/k8s-shield/values.yaml b/charts/k8s-shield/values.yaml new file mode 100644 index 00000000..41f7391e --- /dev/null +++ b/charts/k8s-shield/values.yaml @@ -0,0 +1,157 @@ +#bypassLabel defines a label with a key-value pair that allows the user to apply policies without any restrictions. +bypassLabel: + key: "" + value: "" + +# pvcCreationPolicy controls the creation of Persistent Volume Claims (PVCs). +# If enabled, it restricts PVC creation in specified namespaces. +pvcCreationPolicy: + enabled: true # If true, the policy prevents PVC creation in specified namespaces. + namespaces: {} # List of namespaces where the policy applies. + validationActions: + - Deny + +# namespaceDeletionPolicy restricts the deletion of namespaces. +# When enabled, it prevents deletion of namespaces in specified namespaces. +namespaceDeletionPolicy: + enabled: true # If true, this policy restricts the deletion of namespaces. + namespaces: # List of namespaces that you want to protect from deletion + - kube-system + validationActions: + - Deny + +# loadBalancerCreationPolicy restricts the creation of LoadBalancer type services. +# This policy prevents creating LoadBalancer services in the specified namespaces. +loadBalancerCreationPolicy: + enabled: true # If true, the policy blocks creation of LoadBalancer type services. + namespaces: {} # List of namespaces where the policy applies. + validationActions: + - Deny + +# appDeletionPolicy prevents the deletion of applications within specified namespaces. +# If enabled, applications cannot be deleted in the listed namespaces. +appDeletionPolicy: + enabled: true # If true, this policy restricts application deletion. + namespaces: {} # List of namespaces where the policy applies. + validationActions: + - Deny + +# resourcePolicies enforce the specification of resource requests and limits for deployments, statefulsets, etc. +# The policy ensures resource constraints on all pods in the specified namespaces. +resourcePolicies: + WithoutResource: + enabled: true # If true, this policy enforces that deployments have resource requests and limits. + namespaces: {} # List of namespaces where the policy applies. + validationActions: + - Deny + operations: + - CREATE + - UPDATE + resources: + - pods + - deployments + - statefulsets + - rollouts + apiGroups: + - apps + - rollout.k8s.io + + limitResourcePolicy: + enabled: true # If true, this sub-policy enforces resource limits for deployments. + apiGroups: # API groups where the policy applies. + - "" + - apps + - argoproj.io + namespaces: {} + operations: + - CREATE + - UPDATE + resources: + - pods + - deployments + - statefulsets + - rollouts + maxCPULimit: "1000m" # Maximum CPU limit if not specified. Default is 1000m. + maxMemoryLimit: "2Gi" # Maximum memory limit if not specified. Default is 2Gi. + validationActions: + - Deny + +# adminClusterRoleCreation prevents the creation of new roles and cluster roles with admin access in the cluster. +adminClusterRoleCreation: + enabled: true # If true, this policy prevents creation of new roles and cluster roles. + validationActions: + - Deny + +# cLusterRoleBindingCreation prevents the creation of clusterRoleBindings with cluster-admin access. +cLusterRoleBindingCreation: + enabled: true # If true, this policy prevents creation of clusterRoleBindings with admin access. + validationActions: # Defines the actions when the policy is violated. Default is DENY. + - Deny + +# readinessAndLivenessPolicy ensures that readiness and liveness probes are defined for containers. +# This ensures that pods are only considered healthy when they pass the defined probes. +readinessAndLivenessPolicy: + enabled: true # If true, this policy ensures readiness and liveness probes are defined. + namespaces: {} # List of namespaces where the policy applies. + operations: + - CREATE + - UPDATE + resources: + - deployments + - statefulsets + - rollouts + apiGroups: + - apps + - argoproj.io + validationActions: + - Deny + +# podSecurityPolicy ensures that security-related best practices are followed for Pods. +# For example, it restricts the use of privileged containers and ensures read-only file systems. +podSecurityPolicy: + enabled: true # If true, the Pod Security Policy is enabled. + allowrunAsNonRoot: false # Prevent containers from running as root. + allowreadOnlyRootFilesystem: false # Ensure the root filesystem is not writable. + allowPrivilegeEscalation: false # Prevent processes from gaining more privileges than their parent. + allowprivileged: false # Disables privileged mode for containers. + validationActions: # Defines the actions when the policy is violated. Default is DENY. + - Deny + operations: + - CREATE + - UPDATE + resources: + - pods + - deployments + - statefulsets + - rollouts + - cronjobs + apiGroups: + - "" + - apps + - batch + - rollout.k8s.io + +# containerSecurityPolicy defines security best practices for containers within Pods. +# It ensures that containers adhere to security policies for non-root execution, file system access, and privilege escalation. +containerSecurityPolicy: + enabled: true # If true, the container security policy is enabled. + allowrunAsNonRoot: false # Prevent containers from running as root. + allowreadOnlyRootFilesystem: false # Ensure the root filesystem is not writable. + allowPrivilegeEscalation: false # Prevent processes from gaining more privileges than their parent. + allowprivileged: false # Disable privileged mode to limit container capabilities on the host. + validationActions: # Defines the actions when the policy is violated. Default is DENY. + - Deny + operations: + - CREATE + - UPDATE + resources: + - pods + - deployments + - statefulsets + - rollouts + - cronjobs + apiGroups: + - "" + - apps + - batch + - rollout.k8s.io