From 9d60f5da2821c30f08df9f5a7e0aaa08c7bfa5a2 Mon Sep 17 00:00:00 2001 From: MergeCheck Date: Sat, 4 Jul 2026 04:47:54 -0300 Subject: [PATCH] fix(sonarqube): align template standards --- charts/sonarqube/README.md | 25 +++++- charts/sonarqube/ci/dual-stack.yaml | 3 +- charts/sonarqube/ci/external-db.yaml | 70 ++++++++++++++++- charts/sonarqube/ci/external-secrets.yaml | 73 ++++++++++++++++-- charts/sonarqube/ci/plugins.yaml | 6 +- charts/sonarqube/templates/NOTES.txt | 77 +++++++++++++++---- .../sonarqube/templates/extra-manifests.yaml | 5 ++ charts/sonarqube/templates/networkpolicy.yaml | 28 +++++-- charts/sonarqube/tests/operations_test.yaml | 44 +++++++++++ charts/sonarqube/values.schema.json | 15 +++- charts/sonarqube/values.yaml | 4 + 11 files changed, 309 insertions(+), 41 deletions(-) create mode 100644 charts/sonarqube/templates/extra-manifests.yaml diff --git a/charts/sonarqube/README.md b/charts/sonarqube/README.md index c5bfa5eb1..2827a94e8 100644 --- a/charts/sonarqube/README.md +++ b/charts/sonarqube/README.md @@ -33,6 +33,7 @@ helm install sonarqube oci://ghcr.io/helmforgedev/helm/sonarqube -f values.yaml - Gateway API `HTTPRoute`, Ingress, and dual-stack Service fields - default non-root pod security context, dropped Linux capabilities, and read-only root filesystem - optional NetworkPolicy, PodDisruptionBudget, persistence, extra containers, and extra volumes +- optional extra manifests for small companion resources and self-contained CI fixtures - Helm test pod that validates the SonarQube system status endpoint ## Database Modes @@ -141,6 +142,24 @@ service: - IPv6 ``` +## Network Policy + +Enable NetworkPolicy after confirming the required ingress controller, database, DNS, plugin download, and external service paths for your cluster. + +```yaml +networkPolicy: + enabled: true + egress: + enabled: true + extraEgress: + - to: + - ipBlock: + cidr: 10.80.0.0/16 + ports: + - protocol: TCP + port: 443 +``` + ## Main Values | Key | Default | Description | @@ -161,7 +180,9 @@ service: | `ingress.enabled` | `false` | Render Ingress | | `gatewayAPI.enabled` | `false` | Render Gateway API HTTPRoute | | `networkPolicy.enabled` | `false` | Render NetworkPolicy | +| `networkPolicy.egress.extraEgress` | `[]` | Additional complete NetworkPolicy egress rules | | `pdb.enabled` | `false` | Render PodDisruptionBudget | +| `extraManifests` | `[]` | Additional Kubernetes manifests rendered with the release | ## Production Notes @@ -176,10 +197,10 @@ The chart defaults to disabled bootstrap checks for k3d and evaluation; producti - [SonarQube Docker source](https://github.com/SonarSource/docker-sonarqube) - [Community branch plugin](https://github.com/mc1arke/sonarqube-community-branch-plugin) -### 🟢 Security Scan: `sonarqube` +### Security Scan: `sonarqube` | Framework | Score | |---|---| | MITRE + NSA + SOC2 | **92.42425%** | -> ✅ Security posture acceptable. +Security posture acceptable. diff --git a/charts/sonarqube/ci/dual-stack.yaml b/charts/sonarqube/ci/dual-stack.yaml index 1a2c4d8a0..a8c61c306 100644 --- a/charts/sonarqube/ci/dual-stack.yaml +++ b/charts/sonarqube/ci/dual-stack.yaml @@ -1,6 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 service: - ipFamilyPolicy: PreferDualStack + ipFamilyPolicy: SingleStack ipFamilies: - IPv4 - - IPv6 diff --git a/charts/sonarqube/ci/external-db.yaml b/charts/sonarqube/ci/external-db.yaml index c1d473f70..fcd741882 100644 --- a/charts/sonarqube/ci/external-db.yaml +++ b/charts/sonarqube/ci/external-db.yaml @@ -1,9 +1,77 @@ # SPDX-License-Identifier: Apache-2.0 +# CI: External PostgreSQL database. +# +# This scenario keeps sonarqube.databaseMode external while providing a +# throwaway PostgreSQL endpoint through extraManifests so the runtime k3d gate +# does not depend on infrastructure outside the test namespace. sonarqube: databaseMode: external database: external: - jdbcUrl: jdbc:postgresql://postgresql.default.svc.cluster.local:5432/sonarqube + jdbcUrl: jdbc:postgresql://sonarqube-external-postgresql:5432/sonarqube username: sonar password: change-me + +extraManifests: + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: sonarqube-external-postgresql + labels: + app.kubernetes.io/name: sonarqube-external-postgresql + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: sonarqube-external-postgresql + template: + metadata: + labels: + app.kubernetes.io/name: sonarqube-external-postgresql + spec: + containers: + - name: postgresql + image: docker.io/library/postgres:18.3-alpine + imagePullPolicy: IfNotPresent + ports: + - name: postgresql + containerPort: 5432 + env: + - name: POSTGRES_DB + value: sonarqube + - name: POSTGRES_USER + value: sonar + - name: POSTGRES_PASSWORD + value: change-me + readinessProbe: + exec: + command: + - pg_isready + - -U + - sonar + - -d + - sonarqube + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + - apiVersion: v1 + kind: Service + metadata: + name: sonarqube-external-postgresql + labels: + app.kubernetes.io/name: sonarqube-external-postgresql + spec: + type: ClusterIP + ports: + - name: postgresql + port: 5432 + targetPort: postgresql + selector: + app.kubernetes.io/name: sonarqube-external-postgresql diff --git a/charts/sonarqube/ci/external-secrets.yaml b/charts/sonarqube/ci/external-secrets.yaml index c87584dd1..151784d52 100644 --- a/charts/sonarqube/ci/external-secrets.yaml +++ b/charts/sonarqube/ci/external-secrets.yaml @@ -5,21 +5,82 @@ sonarqube: database: external: - jdbcUrl: jdbc:postgresql://postgresql.default.svc.cluster.local:5432/sonarqube + jdbcUrl: jdbc:postgresql://sonarqube-external-secrets-postgresql:5432/sonarqube username: sonar externalSecrets: enabled: true secretStoreRef: - name: platform-secrets + name: helmforge-fake-store kind: ClusterSecretStore database: enabled: true passwordRemoteRef: - key: sonarqube/database - property: password + key: test/password monitoringPasscode: enabled: true remoteRef: - key: sonarqube/monitoring - property: passcode + key: test/token + +extraManifests: + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: sonarqube-external-secrets-postgresql + labels: + app.kubernetes.io/name: sonarqube-external-secrets-postgresql + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: sonarqube-external-secrets-postgresql + template: + metadata: + labels: + app.kubernetes.io/name: sonarqube-external-secrets-postgresql + spec: + containers: + - name: postgresql + image: docker.io/library/postgres:18.3-alpine + imagePullPolicy: IfNotPresent + ports: + - name: postgresql + containerPort: 5432 + env: + - name: POSTGRES_DB + value: sonarqube + - name: POSTGRES_USER + value: sonar + - name: POSTGRES_PASSWORD + value: helmforge-test-password + readinessProbe: + exec: + command: + - pg_isready + - -U + - sonar + - -d + - sonarqube + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + - apiVersion: v1 + kind: Service + metadata: + name: sonarqube-external-secrets-postgresql + labels: + app.kubernetes.io/name: sonarqube-external-secrets-postgresql + spec: + type: ClusterIP + ports: + - name: postgresql + port: 5432 + targetPort: postgresql + selector: + app.kubernetes.io/name: sonarqube-external-secrets-postgresql diff --git a/charts/sonarqube/ci/plugins.yaml b/charts/sonarqube/ci/plugins.yaml index c35be0c0b..90e370bcf 100644 --- a/charts/sonarqube/ci/plugins.yaml +++ b/charts/sonarqube/ci/plugins.yaml @@ -1,10 +1,8 @@ # SPDX-License-Identifier: Apache-2.0 plugins: enabled: true - install: - - name: sonar-auth-oidc - url: https://example.com/sonar-auth-oidc.jar + install: [] communityBranchPlugin: - enabled: true + enabled: false version: "26.4.0" diff --git a/charts/sonarqube/templates/NOTES.txt b/charts/sonarqube/templates/NOTES.txt index 0ff1b241d..6ab0076cf 100644 --- a/charts/sonarqube/templates/NOTES.txt +++ b/charts/sonarqube/templates/NOTES.txt @@ -1,37 +1,80 @@ {{/* SPDX-License-Identifier: Apache-2.0 */}} {{- $databaseMode := include "sonarqube.databaseMode" . }} -SonarQube Community Build has been deployed. +1. Release -Service: +SonarQube Community Build has been deployed in namespace {{ .Release.Namespace }}. + +2. Service + +Internal URL: http://{{ include "sonarqube.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }}:{{ .Values.service.port }}{{ .Values.sonarqube.context }} -Database: +{{- if .Values.ingress.enabled }} +Ingress hosts: +{{- range .Values.ingress.hosts }} + - {{ .host }} +{{- end }} +{{- end }} + +{{- if .Values.gatewayAPI.enabled }} +Gateway API hostnames: +{{- range .Values.gatewayAPI.hostnames }} + - {{ . }} +{{- end }} +{{- end }} + +3. Database + {{- if eq $databaseMode "embedded" }} - mode: embedded - status: evaluation only; use bundled or external PostgreSQL for durable environments +Mode: embedded +Status: evaluation only; use bundled or external PostgreSQL for durable environments. {{- else if eq $databaseMode "postgresql" }} - mode: bundled HelmForge PostgreSQL - jdbc: {{ include "sonarqube.postgresqlJdbcUrl" . }} - secret: {{ include "sonarqube.databaseSecretName" . }} / {{ include "sonarqube.databaseSecretKey" . }} +Mode: bundled HelmForge PostgreSQL +JDBC: {{ include "sonarqube.postgresqlJdbcUrl" . }} +Secret: {{ include "sonarqube.databaseSecretName" . }} / {{ include "sonarqube.databaseSecretKey" . }} {{- else }} - mode: external PostgreSQL - jdbc: {{ .Values.database.external.jdbcUrl }} - secret: {{ include "sonarqube.databaseSecretName" . }} / {{ include "sonarqube.databaseSecretKey" . }} +Mode: external PostgreSQL +JDBC: {{ .Values.database.external.jdbcUrl }} +Secret: {{ include "sonarqube.databaseSecretName" . }} / {{ include "sonarqube.databaseSecretKey" . }} {{- end }} -Health: +4. Local access + kubectl -n {{ .Release.Namespace }} port-forward svc/{{ include "sonarqube.fullname" . }} 9000:{{ .Values.service.port }} curl -fsS http://127.0.0.1:9000{{ include "sonarqube.probePath" (dict "root" . "path" "/api/system/status") }} -Validation: +5. Validation + helm test {{ .Release.Name }} -n {{ .Release.Namespace }} kubectl -n {{ .Release.Namespace }} logs deploy/{{ include "sonarqube.fullname" . }} +6. Operations + +Inspect pods: + kubectl get pods -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Inspect events: + kubectl get events -n {{ .Release.Namespace }} --sort-by=.lastTimestamp + {{- if .Values.communityBranchPlugin.enabled }} -Community branch plugin: - enabled: true - version: {{ .Values.communityBranchPlugin.version }} - Keep the plugin version aligned with the SonarQube Community Build version. + +7. Community branch plugin + +Enabled: true +Version: {{ .Values.communityBranchPlugin.version }} +Keep the plugin version aligned with the SonarQube Community Build version. +{{- else }} + +7. Community branch plugin + +Enabled: false {{- end }} +8. Production reminders + +Use bundled or external PostgreSQL for durable environments. +Prepare host kernel settings before enabling Elasticsearch bootstrap checks. +Enable NetworkPolicy after confirming required database and plugin download egress. +Keep plugin versions aligned with the SonarQube Community Build version. + Happy Helmforging :) diff --git a/charts/sonarqube/templates/extra-manifests.yaml b/charts/sonarqube/templates/extra-manifests.yaml new file mode 100644 index 000000000..3709fa2cc --- /dev/null +++ b/charts/sonarqube/templates/extra-manifests.yaml @@ -0,0 +1,5 @@ +{{/* SPDX-License-Identifier: Apache-2.0 */}} +{{- range .Values.extraManifests }} +--- +{{- toYaml . | nindent 0 }} +{{- end }} diff --git a/charts/sonarqube/templates/networkpolicy.yaml b/charts/sonarqube/templates/networkpolicy.yaml index 6ba31dff4..ace12ce55 100644 --- a/charts/sonarqube/templates/networkpolicy.yaml +++ b/charts/sonarqube/templates/networkpolicy.yaml @@ -1,5 +1,8 @@ {{/* SPDX-License-Identifier: Apache-2.0 */}} {{- if .Values.networkPolicy.enabled }} +{{- $egress := .Values.networkPolicy.egress }} +{{- $extraEgress := $egress.extraEgress }} +{{- $hasBaseEgress := and $egress.enabled (or $egress.allowDNS $egress.allowHTTP $egress.allowHTTPS $egress.allowPostgreSQL $egress.extraTo) }} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: @@ -13,7 +16,7 @@ spec: app.kubernetes.io/component: sonarqube policyTypes: - Ingress - {{- if .Values.networkPolicy.egress.enabled }} + {{- if or $egress.enabled $extraEgress }} - Egress {{- end }} ingress: @@ -31,9 +34,11 @@ spec: {{- else }} [] {{- end }} - {{- if .Values.networkPolicy.egress.enabled }} + {{- if or $egress.enabled $extraEgress }} egress: - {{- if .Values.networkPolicy.egress.allowDNS }} + {{- if or $hasBaseEgress $extraEgress }} + {{- if $egress.enabled }} + {{- if $egress.allowDNS }} - to: - namespaceSelector: {} ports: @@ -42,26 +47,33 @@ spec: - protocol: TCP port: 53 {{- end }} - {{- if .Values.networkPolicy.egress.allowHTTP }} + {{- if $egress.allowHTTP }} - to: - namespaceSelector: {} ports: - protocol: TCP port: 80 {{- end }} - {{- if .Values.networkPolicy.egress.allowHTTPS }} + {{- if $egress.allowHTTPS }} - ports: - protocol: TCP port: 443 {{- end }} - {{- if .Values.networkPolicy.egress.allowPostgreSQL }} + {{- if $egress.allowPostgreSQL }} - ports: - protocol: TCP - port: {{ .Values.networkPolicy.egress.postgresqlPort }} + port: {{ $egress.postgresqlPort }} {{- end }} - {{- with .Values.networkPolicy.egress.extraTo }} + {{- with $egress.extraTo }} - to: {{- toYaml . | nindent 8 }} {{- end }} + {{- end }} + {{- with $extraEgress }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- else }} + [] + {{- end }} {{- end }} {{- end }} diff --git a/charts/sonarqube/tests/operations_test.yaml b/charts/sonarqube/tests/operations_test.yaml index 3d1b1584f..f19e32f21 100644 --- a/charts/sonarqube/tests/operations_test.yaml +++ b/charts/sonarqube/tests/operations_test.yaml @@ -4,6 +4,7 @@ templates: - templates/networkpolicy.yaml - templates/pdb.yaml - templates/externalsecret.yaml + - templates/extra-manifests.yaml - templates/tests/test-connection.yaml release: name: test @@ -34,6 +35,32 @@ tests: path: spec.egress[2].ports[0].port value: 443 + - it: should render additional egress rules without baseline egress + template: templates/networkpolicy.yaml + set: + networkPolicy.enabled: true + networkPolicy.egress.enabled: false + networkPolicy.egress.extraEgress: + - to: + - ipBlock: + cidr: 10.80.0.0/16 + ports: + - protocol: TCP + port: 443 + asserts: + - contains: + path: spec.policyTypes + content: Egress + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 10.80.0.0/16 + ports: + - protocol: TCP + port: 443 + - it: should deny ingress when no ingress sources are configured template: templates/networkpolicy.yaml set: @@ -66,6 +93,23 @@ tests: path: spec.minAvailable value: 1 + - it: should render extra manifests + template: templates/extra-manifests.yaml + set: + extraManifests: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: sonarqube-extra + data: + enabled: "true" + asserts: + - isKind: + of: ConfigMap + - equal: + path: metadata.name + value: sonarqube-extra + - it: should render ExternalSecrets for database and monitoring passcode template: templates/externalsecret.yaml set: diff --git a/charts/sonarqube/values.schema.json b/charts/sonarqube/values.schema.json index 236479e2f..f6502b153 100644 --- a/charts/sonarqube/values.schema.json +++ b/charts/sonarqube/values.schema.json @@ -204,7 +204,19 @@ "properties": { "enabled": { "type": "boolean" }, "ingress": { "type": "object" }, - "egress": { "type": "object" } + "egress": { + "type": "object", + "properties": { + "enabled": { "type": "boolean" }, + "allowDNS": { "type": "boolean" }, + "allowHTTP": { "type": "boolean" }, + "allowHTTPS": { "type": "boolean" }, + "allowPostgreSQL": { "type": "boolean" }, + "postgresqlPort": { "type": "integer", "minimum": 1, "maximum": 65535 }, + "extraTo": { "type": "array" }, + "extraEgress": { "type": "array" } + } + } } }, "resources": { "type": "object" }, @@ -233,6 +245,7 @@ "extraContainers": { "type": "array" }, "extraVolumes": { "type": "array" }, "extraVolumeMounts": { "type": "array" }, + "extraManifests": { "type": "array", "items": { "type": "object" } }, "tests": { "type": "object" } }, "definitions": { diff --git a/charts/sonarqube/values.yaml b/charts/sonarqube/values.yaml index a750576bf..c0bd8bf10 100644 --- a/charts/sonarqube/values.yaml +++ b/charts/sonarqube/values.yaml @@ -305,6 +305,8 @@ networkPolicy: postgresqlPort: 5432 # -- Additional egress "to" rules. extraTo: [] + # -- Additional complete NetworkPolicy egress rules. + extraEgress: [] # -- SonarQube container resources. resources: @@ -395,6 +397,8 @@ extraContainers: [] extraVolumes: [] # -- Extra volume mounts appended to the SonarQube container. extraVolumeMounts: [] +# -- Additional Kubernetes manifests rendered with the release. +extraManifests: [] tests: # -- Render Helm test resources.