Enhance `externalDatabase` configuration flexibility for secret references
Proposed functionality
Description
Currently, the externalDatabase configuration in the Helm chart only supports referencing a single key from an existing secret (the password). This limitation prevents users from dynamically configuring other database connection parameters when using external database operators that provide comprehensive connection information via secrets.
Current Limitation
The current values.yaml schema only allows referencing the password from an existing secret:
externalDatabase:
host: localhost
port: 5432
database: netbox
username: netbox
password: ""
existingSecretName: ""
existingSecretKey: postgresql-password
Proposed Solution
Enhance the externalDatabase configuration to support referencing multiple keys from existing secrets for all connection parameters:
externalDatabase:
host: localhost
port: 5432
database: netbox
username: netbox
password: ""
# Enhanced secret reference configuration
existingSecret:
name: ""
keys:
host: "HOST" # Optional: reference host from secret
port: "PORT" # Optional: reference port from secret
database: "DATABASE" # Optional: reference database name from secret
username: "LOGIN" # Optional: reference username from secret
password: "PASSWORD" # Optional: reference password from secret
# Backward compatibility
existingSecretName: ""
existingSecretKey: postgresql-password
Implementation Details
The implementation should:
-
Maintain backward compatibility with the existing
existingSecretNameandexistingSecretKeyfields - Support partial secret references - users should be able to reference only specific parameters from secrets while keeping others as static values
- Prioritize secret values over static values when both are provided
- Validate secret references during deployment
Example Usage
With the EasyMile PostgreSQL operator secret shown above, users could configure:
externalDatabase:
existingSecret:
name: "netbox-pg-user"
keys:
host: "HOST"
port: "PORT"
database: "DATABASE"
username: "LOGIN"
password: "PASSWORD"
This would dynamically configure all database connection parameters from the operator-generated secret, enabling full integration with the PostgreSQL operator lifecycle.
Benefits
- Seamless integration with database operators like EasyMile PostgreSQL operator
- Reduced configuration complexity and maintenance overhead
- Enhanced security by avoiding hardcoded connection parameters in values files
- Better GitOps compatibility with dynamic infrastructure provisioning
- Maintained backward compatibility for existing deployments
- Operator lifecycle alignment - connection parameters automatically update when operators modify secrets
Alternative Considered
An alternative approach could be to support secret references directly in each field:
externalDatabase:
host:
value: "localhost"
secretRef:
name: "postgres-secret"
key: "HOST"
# ... similar for other fields
However, the proposed solution is cleaner and more intuitive for users managing comprehensive database secrets from operators.
Use case
I'm using EasyMile PostgreSQL operator, which creates a secret containing all necessary database connection information after provisioning a database instance. Here's an example of the generated secret:
kind: Secret
apiVersion: v1
metadata:
labels:
app: netbox-pg-user-role
name: netbox-pg-user
namespace: netbox
data:
ARGS: ""
DATABASE: bmV0Ym94X2Ri
HOST: cG9zdGdyZXMtc2VydmljZQ==
LOGIN: bmV0Ym94X3VzZXI=
PASSWORD: c3VwZXJfc2VjcmV0X3Bhc3N3b3Jk
PORT: NTQzMg==
POSTGRES_URL: cG9zdGdyZXNxbDovL25ldGJveF91c2VyOnN1cGVyX3NlY3JldF9wYXNzd29yZEBwb3N0Z3Jlcy1zZXJ2aWNlOjU0MzIvbmV0Ym94X2Ri
POSTGRES_URL_ARGS: ""
Currently, I can only reference the PASSWORD from this secret, forcing me to:
- Manually decode the base64-encoded secret data to extract connection parameters
-
Hardcode these decoded values in my
values.yamlfile forhost,port,database, andusernamefields - Maintain these hardcoded values manually whenever the operator updates the database configuration
This workflow is error-prone, defeats the purpose of having a comprehensive secret generated by the operator, and breaks the automation benefits of using a database operator. For example, I currently have to run:
kubectl get secret netbox-pg-user -o jsonpath='{.data.HOST}' | base64 -d
kubectl get secret netbox-pg-user -o jsonpath='{.data.PORT}' | base64 -d
kubectl get secret netbox-pg-user -o jsonpath='{.data.DATABASE}' | base64 -d
kubectl get secret netbox-pg-user -o jsonpath='{.data.LOGIN}' | base64 -d
Then manually copy these decoded values into my Helm values, which is neither scalable nor maintainable.
I am open to implementing this feature if maintainers approve this proposal.
Please let me know, I'll do a PR. Also, if you have a different approach that would be equivalent to this proposal, don't hesitate to give your thoughts. We can discuss it! :)
Thanks for filing this proposal, @katt-999. I'm not sure the number of changes to match this use case will be reasonable. What would be your implementation approach?
Hey @LeoColomb,
My approach will be :
On python script file into charts/netbox/files/configuration.py, we can do database setup like this :
DATABASES["default"]["NAME"] = _read_secret(provided_secret_name, "db_name")
DATABASES["default"]["USER"] = _read_secret(provided_secret_name, "db_user")
DATABASES["default"]["PASSWORD"] = _read_secret(provided_secret_name, "db_password")
DATABASES["default"]["HOST"] = _read_secret(provided_secret_name, "db_host")
DATABASES["default"]["USER"] = _read_secret(provided_secret_name, "db_post")
Then in netbox deployment template :
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "common.names.fullname" . }}
namespace: {{ include "common.names.namespace" . | quote }}
[...]
spec:
[...]
template:
[...]
spec:
[...]
volumes:
[...]
- name: secrets
projected:
sources:
[...]
- secret:
name: {{ include "netbox.postgresql.existingSecret.name" . | quote }}
items:
- key: {{ include "netbox.postgresql.existingSecret.keys.username" . | quote }}
path: db_username
- key: {{ include "netbox.postgresql.existingSecret.keys.password" . | quote }}
path: db_password
- key: {{ include "netbox.postgresql.existingSecret.keys.dbName" . | quote }}
path: db_name
- key: {{ include "netbox.postgresql.existingSecret.keys.host" . | quote }}
path: db_host
- key: {{ include "netbox.postgresql.existingSecret.keys.port" . | quote }}
path: db_port
So in values.yaml :
externalDatabase:
existingSecret:
name: "netbox-pg-user"
keys:
host: "HOST"
port: "PORT"
database: "DATABASE"
username: "LOGIN"
password: "PASSWORD"
_helpers.tpl and values.schema.json must also be updated to reflect that proposal.
I don't know if it works, I haven't test yet. What's your thoughts ?
@katt-999: I like this idea. I'm not running enough environments for this to be an issue, but it'd be nice to have. I'd suggest following the alternative example if implemented, if only because it's closer to kubernetes native syntax.
@LeoColomb:
Thanks for filing this proposal, @katt-999. I'm not sure the number of changes to match this use case will be reasonable. What would be your implementation approach?
What is a reasonable number of changes to make and/or suggest improvements?
Thanks for your comment @mld.
I'll be honest, both solutions seem far too complex to be merged as is.
An acceptable alternative we could consider is allowing templates in each field, using the common.tplvalues.render helper.
Thus, any usual Helm template can be used, secret reading included.
Let me know if you'd like to submit a pull request.