Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion charts/generic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ See the [examples/](examples/) directory for complete, ready-to-use values files
| `service.extraPorts` | Additional service ports | `[]` |
| `services` | Additional Service resources | `[]` |
| `ingress.enabled` | Enable Ingress | `false` |
| `ingress.ingressClassName` | Ingress class | `traefik` |
| `ingress.ingressClassName` | Ingress class. Set `""` to omit `spec.ingressClassName`. | `traefik` |
| `ingress.hosts` | Ingress host rules; `host` supports tpl (e.g. `{{ .Release.Namespace }}`); paths require explicit `backend` when `service.enabled=false` | `[]` |
| `ingress.defaultBackend` | Ingress default backend | `{}` |
| `ingress.tls` | TLS configuration | `[]` |
Expand Down Expand Up @@ -464,6 +464,7 @@ See the [examples/](examples/) directory for complete, ready-to-use values files
| `rbac.clusterRole.create` | Create ClusterRole and ClusterRoleBinding | `false` |
| `networkPolicy.enabled` | Create NetworkPolicy | `false` |
| `networkPolicy.defaultDeny` | Render default-deny policy shape | `false` |
| `networkPolicy.extraEgress` | Additional egress rules appended after `networkPolicy.egress` | `[]` |
| **Batch** | | |
| `jobs` | One-time Job definitions | `[]` |
| `cronjobs` | CronJob definitions | `[]` |
Expand Down
5 changes: 4 additions & 1 deletion charts/generic/docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ Use `externalSecrets.enabled` or `sealedSecrets.enabled` only when the matching

## NetworkPolicy

`networkPolicy.enabled` creates a NetworkPolicy targeting the release pods. Use `networkPolicy.defaultDeny: true` to start from a deny posture and add explicit ingress/egress rules.
`networkPolicy.enabled` creates a NetworkPolicy targeting the release pods. Use `networkPolicy.defaultDeny: true` to
start from a deny posture and add explicit ingress/egress rules. `networkPolicy.extraEgress` appends additional egress
rules after the base `networkPolicy.egress` list, which is useful for environment-specific DNS, proxy, or API-server
allowances without replacing the base policy.

<!-- @AI-METADATA
type: chart-docs
Expand Down
90 changes: 90 additions & 0 deletions charts/generic/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,93 @@ dnsConfig:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- end -}}

{{- define "chart.validate" -}}
{{- $namePattern := "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" -}}
{{- range .Values.containers | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "containers[].name must be a DNS-1123 label: %s" .name) }}
{{- end }}
{{- if gt (len .name) 63 }}
{{- fail (printf "containers[].name must be 63 characters or fewer: %s" .name) }}
{{- end }}
{{- end }}
{{- range .Values.jobs | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "jobs[].name must be a DNS-1123 label: %s" .name) }}
{{- end }}
{{- if gt (len .name) 63 }}
{{- fail (printf "jobs[].name must be 63 characters or fewer: %s" .name) }}
{{- end }}
{{- end }}
{{- range .Values.cronjobs | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "cronjobs[].name must be a DNS-1123 label: %s" .name) }}
{{- end }}
{{- if gt (len .name) 63 }}
{{- fail (printf "cronjobs[].name must be 63 characters or fewer: %s" .name) }}
{{- end }}
{{- if not .schedule }}
{{- fail (printf "cronjobs[%s].schedule is required" .name) }}
{{- end }}
{{- end }}
{{- range .Values.configMaps | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "configMaps[].name must be a DNS-1123 label: %s" .name) }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{{- end }}
{{- if gt (len .name) 63 }}
{{- fail (printf "configMaps[].name must be 63 characters or fewer: %s" .name) }}
{{- end }}
{{- end }}
{{- if and .Values.hpa.enabled (eq (.Values.workload.type | default "Deployment") "DaemonSet") }}
{{- fail "hpa.enabled cannot be used with workload.type=DaemonSet" }}
{{- end }}
{{- if and .Values.hpa.enabled (not .Values.hpa.maxReplicas) }}
{{- fail "hpa.maxReplicas is required when hpa.enabled=true" }}
{{- end }}
{{- $pdbHasMinAvailable := hasKey .Values.pdb "minAvailable" -}}
{{- $pdbHasMaxUnavailable := hasKey .Values.pdb "maxUnavailable" -}}
{{- if and .Values.pdb.enabled (not $pdbHasMinAvailable) (not $pdbHasMaxUnavailable) }}
{{- fail "pdb.minAvailable or pdb.maxUnavailable is required when pdb.enabled=true" }}
{{- end }}
{{- if and .Values.pdb.enabled $pdbHasMinAvailable $pdbHasMaxUnavailable }}
{{- fail "set only one of pdb.minAvailable or pdb.maxUnavailable" }}
{{- end }}
{{- if and .Values.serviceMonitor.enabled (not .Values.serviceMonitor.endpoints) }}
{{- fail "serviceMonitor.endpoints is required when serviceMonitor.enabled=true" }}
{{- end }}
{{- if and .Values.ingress.enabled (not .Values.ingress.hosts) (not .Values.ingress.defaultBackend) }}
{{- fail "ingress.hosts or ingress.defaultBackend is required when ingress.enabled=true" }}
{{- end }}
{{- if and .Values.ingress.enabled (eq .Values.service.enabled false) }}
{{- range .Values.ingress.hosts | default list }}
{{- range .paths | default (list "/") }}
{{- if or (kindIs "string" .) (not .backend) }}
{{- fail "ingress paths require an explicit backend when service.enabled=false" }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- if and .Values.gatewayApi.enabled (eq .Values.service.enabled false) }}
{{- range .Values.gatewayApi.httpRoutes | default list }}
{{- range .rules | default list }}
{{- if not .backendRefs }}
{{- fail "gatewayApi.httpRoutes[].rules[].backendRefs is required when service.enabled=false" }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- if and .Values.keda.enabled .Values.keda.scaledObject.enabled (not (include "chart.hasWorkload" .)) }}
{{- fail "keda.scaledObject.enabled requires workload.enabled=true" }}
{{- end }}
{{- range .Values.persistence.storage | default list }}
{{- if not .capacity }}
{{- fail (printf "persistence.storage[%s].capacity is required" .name) }}
{{- end }}
{{- end }}
{{- range .Values.persistence.persistentVolumeClaims | default list }}
{{- if not .storage }}
{{- fail (printf "persistence.persistentVolumeClaims[%s].storage is required" .name) }}
{{- end }}
{{- end }}
{{- end -}}
4 changes: 3 additions & 1 deletion charts/generic/templates/ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ metadata:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.ingressClassName | default "traefik" }}
{{- with .Values.ingress.ingressClassName }}
ingressClassName: {{ . }}
{{- end }}
{{- with .Values.ingress.defaultBackend }}
defaultBackend:
{{- toYaml . | nindent 4 }}
Expand Down
5 changes: 3 additions & 2 deletions charts/generic/templates/networkpolicy.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{{/* SPDX-License-Identifier: Apache-2.0 */}}
{{- if .Values.networkPolicy.enabled }}
{{- $egressRules := concat (.Values.networkPolicy.egress | default list) (.Values.networkPolicy.extraEgress | default list) }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
Expand All @@ -17,7 +18,7 @@ spec:
policyTypes:
{{- if .Values.networkPolicy.policyTypes }}
{{- toYaml .Values.networkPolicy.policyTypes | nindent 4 }}
{{- else if .Values.networkPolicy.defaultDeny }}
{{- else if or .Values.networkPolicy.defaultDeny $egressRules }}
- Ingress
- Egress
{{- else }}
Expand All @@ -27,7 +28,7 @@ spec:
ingress:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.networkPolicy.egress }}
{{- with $egressRules }}
egress:
Comment thread
mberlofa marked this conversation as resolved.
{{- toYaml . | nindent 4 }}
{{- end }}
Expand Down
4 changes: 2 additions & 2 deletions charts/generic/templates/pdb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ metadata:
labels:
{{- include "chart.labels" . | nindent 4 }}
spec:
{{- if .Values.pdb.maxUnavailable }}
{{- if hasKey .Values.pdb "maxUnavailable" }}
maxUnavailable: {{ .Values.pdb.maxUnavailable }}
{{- else if .Values.pdb.minAvailable }}
{{- else if hasKey .Values.pdb "minAvailable" }}
minAvailable: {{ .Values.pdb.minAvailable }}
{{- end }}
selector:
Expand Down
76 changes: 2 additions & 74 deletions charts/generic/templates/validate.yaml
Original file line number Diff line number Diff line change
@@ -1,74 +1,2 @@
{{/* SPDX-License-Identifier: Apache-2.0 */}}
{{- $namePattern := "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" -}}
{{- range .Values.containers | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "containers[].name must be a DNS-1123 label: %s" .name) }}
{{- end }}
{{- end }}
{{- range .Values.jobs | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "jobs[].name must be a DNS-1123 label: %s" .name) }}
{{- end }}
{{- end }}
{{- range .Values.cronjobs | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "cronjobs[].name must be a DNS-1123 label: %s" .name) }}
{{- end }}
{{- if not .schedule }}
{{- fail (printf "cronjobs[%s].schedule is required" .name) }}
{{- end }}
{{- end }}
{{- range .Values.configMaps | default list }}
{{- if not (regexMatch $namePattern .name) }}
{{- fail (printf "configMaps[].name must be a DNS-1123 label: %s" .name) }}
{{- end }}
{{- end }}
{{- if and .Values.hpa.enabled (eq (.Values.workload.type | default "Deployment") "DaemonSet") }}
{{- fail "hpa.enabled cannot be used with workload.type=DaemonSet" }}
{{- end }}
{{- if and .Values.hpa.enabled (not .Values.hpa.maxReplicas) }}
{{- fail "hpa.maxReplicas is required when hpa.enabled=true" }}
{{- end }}
{{- if and .Values.pdb.enabled (not .Values.pdb.minAvailable) (not .Values.pdb.maxUnavailable) }}
{{- fail "pdb.minAvailable or pdb.maxUnavailable is required when pdb.enabled=true" }}
{{- end }}
{{- if and .Values.pdb.enabled .Values.pdb.minAvailable .Values.pdb.maxUnavailable }}
{{- fail "set only one of pdb.minAvailable or pdb.maxUnavailable" }}
{{- end }}
{{- if and .Values.serviceMonitor.enabled (not .Values.serviceMonitor.endpoints) }}
{{- fail "serviceMonitor.endpoints is required when serviceMonitor.enabled=true" }}
{{- end }}
{{- if and .Values.ingress.enabled (not .Values.ingress.hosts) (not .Values.ingress.defaultBackend) }}
{{- fail "ingress.hosts or ingress.defaultBackend is required when ingress.enabled=true" }}
{{- end }}
{{- if and .Values.ingress.enabled (eq .Values.service.enabled false) }}
{{- range .Values.ingress.hosts | default list }}
{{- range .paths | default (list "/") }}
{{- if or (kindIs "string" .) (not .backend) }}
{{- fail "ingress paths require an explicit backend when service.enabled=false" }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- if and .Values.gatewayApi.enabled (eq .Values.service.enabled false) }}
{{- range .Values.gatewayApi.httpRoutes | default list }}
{{- range .rules | default list }}
{{- if not .backendRefs }}
{{- fail "gatewayApi.httpRoutes[].rules[].backendRefs is required when service.enabled=false" }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- if and .Values.keda.enabled .Values.keda.scaledObject.enabled (not (include "chart.hasWorkload" .)) }}
{{- fail "keda.scaledObject.enabled requires workload.enabled=true" }}
{{- end }}
{{- range .Values.persistence.storage | default list }}
{{- if not .capacity }}
{{- fail (printf "persistence.storage[%s].capacity is required" .name) }}
{{- end }}
{{- end }}
{{- range .Values.persistence.persistentVolumeClaims | default list }}
{{- if not .storage }}
{{- fail (printf "persistence.persistentVolumeClaims[%s].storage is required" .name) }}
{{- end }}
{{- end }}
# SPDX-License-Identifier: Apache-2.0
{{- include "chart.validate" . -}}
2 changes: 1 addition & 1 deletion charts/generic/tests/externalsecret_test.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
suite: ExternalSecret
templates:
- external-secret.yaml
- externalsecret.yaml
release:
name: test
namespace: default
Expand Down
13 changes: 13 additions & 0 deletions charts/generic/tests/ingress_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,16 @@ tests:
- equal:
path: spec.tls[0].secretName
value: api-staging-tls

- it: should omit ingressClassName when empty
set:
ingress.enabled: true
ingress.ingressClassName: ""
ingress.hosts:
- host: app.example.com
paths:
- path: /
pathType: Prefix
asserts:
- notExists:
path: spec.ingressClassName
9 changes: 9 additions & 0 deletions charts/generic/tests/pdb_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ tests:
asserts:
- isKind:
of: PodDisruptionBudget

- it: should render zero maxUnavailable when explicitly set
set:
pdb.enabled: true
pdb.maxUnavailable: 0
asserts:
- equal:
path: spec.maxUnavailable
value: 0
31 changes: 31 additions & 0 deletions charts/generic/tests/security_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,37 @@ tests:
content: Egress
template: networkpolicy.yaml

- it: should append extra NetworkPolicy egress rules
set:
networkPolicy:
enabled: true
egress:
- to:
- podSelector:
matchLabels:
app: database
extraEgress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
asserts:
- equal:
path: spec.egress[0].to[0].podSelector.matchLabels.app
value: database
template: networkpolicy.yaml
- equal:
path: spec.egress[1].ports[0].port
value: 53
template: networkpolicy.yaml
- contains:
path: spec.policyTypes
content: Egress
template: networkpolicy.yaml

- it: should apply restricted security preset when contexts are not explicit
set:
securityPreset: restricted
Expand Down
16 changes: 16 additions & 0 deletions charts/generic/tests/validation_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ tests:
- failedTemplate:
errorMessage: "containers[].name must be a DNS-1123 label: App"

- it: should fail when a container name is longer than 63 characters
set:
containers:
- name: app-name-that-is-intentionally-longer-than-sixty-three-characters-total
asserts:
- failedTemplate:
errorMessage: "containers[].name must be 63 characters or fewer: app-name-that-is-intentionally-longer-than-sixty-three-characters-total"

- it: should fail when HPA targets a DaemonSet
set:
workload.type: DaemonSet
Expand All @@ -37,6 +45,14 @@ tests:
- failedTemplate:
errorMessage: "pdb.minAvailable or pdb.maxUnavailable is required when pdb.enabled=true"

- it: should allow zero as an explicit PDB availability budget
set:
pdb.enabled: true
pdb.minAvailable: 0
asserts:
- hasDocuments:
count: 0

- it: should fail when ingress falls back to disabled primary service
set:
service.enabled: false
Expand Down
7 changes: 6 additions & 1 deletion charts/generic/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,12 @@
"podSelector": { "type": "object" },
"policyTypes": { "type": "array", "items": { "type": "string" } },
"ingress": { "type": "array", "items": { "type": "object" } },
"egress": { "type": "array", "items": { "type": "object" } }
"egress": { "type": "array", "items": { "type": "object" } },
"extraEgress": {
"type": "array",
"description": "Additional egress rules appended after networkPolicy.egress",
"items": { "type": "object" }
}
}
},
"jobs": {
Expand Down
2 changes: 2 additions & 0 deletions charts/generic/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ networkPolicy:
policyTypes: []
ingress: []
egress: []
# -- Additional egress rules appended after `networkPolicy.egress`.
extraEgress: []

# =============================================================================
# ServiceMonitor (Prometheus Operator)
Expand Down