Problem
We use the default CNPG Secret, which provides all the data needed for accessing the database. It has the following structure:
apiVersion: v1
data:
dbname: YXBw
fqdn-jdbc-uri: amRiYzpwb3N0Z3Jlc3FsOi8vc29uYXJxdWJlLWNucGctY2x1c3Rlci1ydy5zb25hcnF1YmUuc3ZjLmNsdXN0ZXIubG9jYWw6NTQzMi9hcHA/cGFzc3dvcmQ9SHh5M1c2QUdxdHhvbkNtV3N4WUpiU3FoVXVMTkZRSGU2VUZ2eWQ4R0ZnOXFnQldRZTg4cks3RnY5SkRzVXA5SyZ1c2VyPWFwcA==
fqdn-uri: cG9zdGdyZXNxbDovL2FwcDpIeHkzVzZBR3F0eG9uQ21Xc3hZSmJTcWhVdUxORlFIZTZVRnZ5ZDhHRmc5cWdCV1FlODhySzdGdjlKRHNVcDlLQHNvbmFycXViZS1jbnBnLWNsdXN0ZXItcncuc29uYXJxdWJlLnN2Yy5jbHVzdGVyLmxvY2FsOjU0MzIvYXBw
host: c29uYXJxdWJlLWNucGctY2x1c3Rlci1ydw==
jdbc-uri: amRiYzpwb3N0Z3Jlc3FsOi8vc29uYXJxdWJlLWNucGctY2x1c3Rlci1ydy5zb25hcnF1YmU6NTQzMi9hcHA/cGFzc3dvcmQ9SHh5M1c2QUdxdHhvbkNtV3N4WUpiU3FoVXVMTkZRSGU2VUZ2eWQ4R0ZnOXFnQldRZTg4cks3RnY5SkRzVXA5SyZ1c2VyPWFwcA==
password: SHh5M1c2QUdxdHhvbkNtV3N4WUpiU3FoVXVMTkZRSGU2VUZ2eWQ4R0ZnOXFnQldRZTg4cks3RnY5SkRzVXA5Sw==
pgpass: c29uYXJxdWJlLWNucGctY2x1c3Rlci1ydzo1NDMyOmFwcDphcHA6SHh5M1c2QUdxdHhvbkNtV3N4WUpiU3FoVXVMTkZRSGU2VUZ2eWQ4R0ZnOXFnQldRZTg4cks3RnY5SkRzVXA5Swo=
port: NTQzMg==
uri: cG9zdGdyZXNxbDovL2FwcDpIeHkzVzZBR3F0eG9uQ21Xc3hZSmJTcWhVdUxORlFIZTZVRnZ5ZDhHRmc5cWdCV1FlODhySzdGdjlKRHNVcDlLQHNvbmFycXViZS1jbnBnLWNsdXN0ZXItcncuc29uYXJxdWJlOjU0MzIvYXBw
user: YXBw
username: YXBw
kind: Secret
metadata:
annotations:
cnpg.io/operatorVersion: 1.27.0
creationTimestamp: "2025-12-18T08:32:24Z"
labels:
cnpg.io/cluster: sonarqube-cnpg-cluster
cnpg.io/reload: "true"
cnpg.io/userType: app
name: sonarqube-cnpg-cluster-app
namespace: sonarqube
ownerReferences:
- apiVersion: postgresql.cnpg.io/v1
controller: true
kind: Cluster
name: sonarqube-cnpg-cluster
uid: 1f57fc23-933c-4414-98c4-3a99a0bbb861
resourceVersion: "29074989"
uid: 4308dd21-d7c6-4595-abb2-a662c7029405
type: kubernetes.io/basic-auth
Decoded:
dbname: app
fqdn-jdbc-uri: jdbc:postgresql://sonarqube-cnpg-cluster-rw.sonarqube.svc.cluster.local:5432/app?password=Hxy3W6AGqtxonCmWsxYJbSqhUuLNFQHe6UFvyd8GFg9qgBWQe88rK7Fv9JDsUp9K&user=app
fqdn-uri: postgresql://app:Hxy3W6AGqtxonCmWsxYJbSqhUuLNFQHe6UFvyd8GFg9qgBWQe88rK7Fv9JDsUp9K@sonarqube-cnpg-cluster-rw.sonarqube.svc.cluster.local:5432/app
host: sonarqube-cnpg-cluster-rw
jdbc-uri: jdbc:postgresql://sonarqube-cnpg-cluster-rw.sonarqube:5432/app?password=Hxy3W6AGqtxonCmWsxYJbSqhUuLNFQHe6UFvyd8GFg9qgBWQe88rK7Fv9JDsUp9K&user=app
password: Hxy3W6AGqtxonCmWsxYJbSqhUuLNFQHe6UFvyd8GFg9qgBWQe88rK7Fv9JDsUp9K
pgpass: |
sonarqube-cnpg-cluster-rw:5432:app:app:Hxy3W6AGqtxonCmWsxYJbSqhUuLNFQHe6UFvyd8GFg9qgBWQe88rK7Fv9JDsUp9K
port: "5432"
uri: postgresql://app:Hxy3W6AGqtxonCmWsxYJbSqhUuLNFQHe6UFvyd8GFg9qgBWQe88rK7Fv9JDsUp9K@sonarqube-cnpg-cluster-rw.sonarqube:5432/app
user: app
username: app
If we try to use the following Helm chart values to set the JDBC string:
jdbcOverwrite:
enabled: true
{{ $cnpgClusterCredentials := exec "kubectl" (list "get" "secret" "sonarqube-cnpg-cluster-app" "-n" "sonarqube" "-o" "json") | fromJson }}
jdbcUrl: {{ index $cnpgClusterCredentials.data "jdbc-uri" | b64dec }}
jdbcUsername: {{ $cnpgClusterCredentials.data.user | b64dec }}
jdbcSecretName: sonarqube-cnpg-cluster-app
jdbcSecretPasswordKey: password
The JDBC URL exposes the password in a ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: sonarqube-sonarqube-jdbc-config
labels:
app: sonarqube
chart: sonarqube-2025.4.2
release: sonarqube
heritage: Helm
data:
- SONAR_JDBC_USERNAME: "postgres"
- SONAR_JDBC_URL: "jdbc:postgresql://sonarqube-cnpg-cluster-rw.sonarqube:5432/\*?password=tKbo9ouXd73Li4vyrg5RadzyTz6etKtMNcB0igG4KXeDPUZNeso9NOYKEWK5iBPl&user=postgres"
+ SONAR_JDBC_USERNAME: "app"
+ SONAR_JDBC_URL: "jdbc:postgresql://sonarqube-cnpg-cluster-rw.sonarqube:5432/app?password=Hxy3W6AGqtxonCmWsxYJbSqhUuLNFQHe6UFvyd8GFg9qgBWQe88rK7Fv9JDsUp9K&user=app"
This can be worked around by performing manipulations with Helm templating, but it would be nice not to expose credentials in this way, as I only discovered this issue while debugging. Potentially, anyone who has access to ConfigMaps in the Kubernetes cluster can gain access to the SonarQube database.
This in development environment, so i can expose credentials in ticket