Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
IvanHunters
c2bad2ab5d
chore(postgres): regenerate postgres.yaml after rebase
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:46:39 +03:00
IvanHunters
a753088fb4
fix(kubevirt-operator): escape Prometheus template variables in PrometheusRule
Helm was trying to evaluate {{ $labels.xxx }} as Helm variables, causing
"undefined variable" errors during E2E tests. Wrap Prometheus template
expressions in backticks to pass them through Helm unchanged.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:43 +03:00
IvanHunters
fac0622499
fix(postgres): make security denylist case-insensitive
PostgreSQL parameter names are case-insensitive, but the denylist
check was case-sensitive, allowing bypass via mixed-case names
(e.g., ARCHIVE_COMMAND instead of archive_command).

Normalize parameter names to lowercase before checking against the
denylist to prevent security bypass.

Thanks to CodeRabbit for catching this security issue.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:43 +03:00
IvanHunters
96c7c16c21
fix(postgres): correct merge argument order for parameter precedence
Fix regression where defaults were overriding user-supplied parameters.
Sprig merge gives precedence to first argument, so user values must be
merged before defaults to maintain correct override behavior.

Before: merge (dict) $defaults .Values.postgresql.parameters
After:  merge (dict) .Values.postgresql.parameters $defaults

Now max_wal_senders and other user parameters correctly override defaults.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:42 +03:00
ohotnikov.ivan
f8001e23b6
fix(postgres): address review feedback
- Regenerate deepcopy artifacts to remove orphaned PostgreSQLParameters methods
- Update e2e test to use string value for max_connections parameter
- Fix template to avoid .Values mutation and use quote function for safe YAML escaping

Signed-off-by: ohotnikov.ivan <ohotnikov.ivan@e-queo.net>
2026-04-30 01:43:42 +03:00
IvanHunters
fb0ed08565
feat(postgres): support all PostgreSQL configuration parameters
Allow users to configure any PostgreSQL server parameter via the
postgresql.parameters field. All parameter values must be provided
as strings (numeric values should be quoted).

Security measures:
- Block parameters that allow shell command execution (archive_command,
  restore_command, ssl_passphrase_command, log_destination, event_source)
- Validation implemented in templates to prevent security vulnerabilities

Generated files updated:
- types.go: Add Parameters field with map[string]string type
- README.md: Add parameter documentation and warnings
- values.schema.json: Update schema for new field
- CRD: Update OpenAPI schema with new field and reordered properties

CloudNativePG manages certain parameters automatically (archive_mode,
primary_conninfo, wal_level). Users should not override these as it
may break backup/replication functionality.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:41 +03:00
10 changed files with 92 additions and 91 deletions

View file

@ -112,15 +112,9 @@ type DatabaseRoles struct {
}
type PostgreSQL struct {
// PostgreSQL server parameters.
// +kubebuilder:default:={}
Parameters PostgreSQLParameters `json:"parameters,omitempty"`
}
type PostgreSQLParameters struct {
// Maximum number of concurrent connections to the database server.
// +kubebuilder:default:=100
MaxConnections int `json:"max_connections,omitempty"`
// PostgreSQL server parameters. All values must be strings (enclose numbers in quotes). See PostgreSQL documentation for available parameters. WARNING: Some parameters are managed by CloudNativePG and should NOT be overridden (archive_mode, archive_command, restore_command, primary_conninfo, etc.). Incorrect values may break backup/replication or require pod restart.
// +kubebuilder:default:={"max_connections":"100"}
Parameters map[string]string `json:"parameters,omitempty"`
}
type Quorum struct {

View file

@ -85,7 +85,7 @@ func (in *ConfigSpec) DeepCopyInto(out *ConfigSpec) {
*out = *in
in.Resources.DeepCopyInto(&out.Resources)
out.Size = in.Size.DeepCopy()
out.Postgresql = in.Postgresql
in.Postgresql.DeepCopyInto(&out.Postgresql)
out.Quorum = in.Quorum
if in.Users != nil {
in, out := &in.Users, &out.Users
@ -164,7 +164,13 @@ func (in *DatabaseRoles) DeepCopy() *DatabaseRoles {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PostgreSQL) DeepCopyInto(out *PostgreSQL) {
*out = *in
out.Parameters = in.Parameters
if in.Parameters != nil {
in, out := &in.Parameters, &out.Parameters
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQL.
@ -177,21 +183,6 @@ func (in *PostgreSQL) DeepCopy() *PostgreSQL {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PostgreSQLParameters) DeepCopyInto(out *PostgreSQLParameters) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgreSQLParameters.
func (in *PostgreSQLParameters) DeepCopy() *PostgreSQLParameters {
if in == nil {
return nil
}
out := new(PostgreSQLParameters)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Quorum) DeepCopyInto(out *Quorum) {
*out = *in

View file

@ -16,7 +16,7 @@ spec:
storageClass: ""
postgresql:
parameters:
max_connections: 100
max_connections: "100"
quorum:
minSyncReplicas: 0
maxSyncReplicas: 0

View file

@ -81,11 +81,10 @@ See:
### Application-specific parameters
| Name | Description | Type | Value |
| --------------------------------------- | ---------------------------------------------------------------- | -------- | ----- |
| `postgresql` | PostgreSQL server configuration. | `object` | `{}` |
| `postgresql.parameters` | PostgreSQL server parameters. | `object` | `{}` |
| `postgresql.parameters.max_connections` | Maximum number of concurrent connections to the database server. | `int` | `100` |
| Name | Description | Type | Value |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ----- |
| `postgresql` | PostgreSQL server configuration. | `object` | `{}` |
| `postgresql.parameters` | PostgreSQL server parameters. All values must be strings (enclose numbers in quotes). See PostgreSQL documentation for available parameters. WARNING: Some parameters are managed by CloudNativePG and should NOT be overridden (archive_mode, archive_command, restore_command, primary_conninfo, etc.). Incorrect values may break backup/replication or require pod restart. | `map[string]string` | `{}` |
### Quorum-based synchronous replication

View file

@ -60,9 +60,16 @@ spec:
{{- end }}
postgresql:
parameters:
max_wal_senders: "30"
{{- with .Values.postgresql.parameters.max_connections }}
max_connections: "{{ . }}"
{{- $defaults := dict "max_wal_senders" "30" }}
{{- $params := merge (dict) .Values.postgresql.parameters $defaults }}
{{- /* Security: Block parameters that could allow shell command execution */ -}}
{{- $dangerousParams := list "archive_command" "restore_command" "ssl_passphrase_command" "log_destination" "event_source" }}
{{- range $key, $value := $params }}
{{- $normalizedKey := lower $key }}
{{- if has $normalizedKey $dangerousParams }}
{{- fail (printf "SECURITY: Parameter '%s' is blocked for security reasons. This parameter could allow arbitrary shell command execution and is managed by CloudNativePG operator." $key) }}
{{- end }}
{{ $key }}: {{ $value | quote }}
{{- end }}
minSyncReplicas: {{ .Values.quorum.minSyncReplicas }}

View file

@ -97,15 +97,13 @@
"default": {},
"properties": {
"parameters": {
"description": "PostgreSQL server parameters.",
"description": "PostgreSQL server parameters. All values must be strings (enclose numbers in quotes). See PostgreSQL documentation for available parameters. WARNING: Some parameters are managed by CloudNativePG and should NOT be overridden (archive_mode, archive_command, restore_command, primary_conninfo, etc.). Incorrect values may break backup/replication or require pod restart.",
"type": "object",
"default": {},
"properties": {
"max_connections": {
"description": "Maximum number of concurrent connections to the database server.",
"type": "integer",
"default": 100
}
"default": {
"max_connections": "100"
},
"additionalProperties": {
"type": "string"
}
}
}

View file

@ -48,16 +48,32 @@ version: v18
## @section Application-specific parameters
##
## @typedef {struct} PostgreSQLParameters - PostgreSQL server parameters.
## @field {int} [max_connections] - Maximum number of concurrent connections to the database server.
## @typedef {struct} PostgreSQL - PostgreSQL server configuration.
## @field {PostgreSQLParameters} [parameters] - PostgreSQL server parameters.
## @field {map[string]string} [parameters] - PostgreSQL server parameters. All values must be strings (enclose numbers in quotes). See PostgreSQL documentation for available parameters. WARNING: Some parameters are managed by CloudNativePG and should NOT be overridden (archive_mode, archive_command, restore_command, primary_conninfo, etc.). Incorrect values may break backup/replication or require pod restart.
## @param {PostgreSQL} postgresql - PostgreSQL server configuration.
postgresql:
parameters:
max_connections: 100
max_connections: "100"
## Additional parameters examples:
## shared_buffers: "1GB"
## effective_cache_size: "4GB"
## bgwriter_delay: "200ms"
## commit_delay: "100"
## commit_siblings: "5"
## work_mem: "256MB"
##
## SECURITY: The following parameters are BLOCKED for security reasons
## (could allow arbitrary shell command execution):
## archive_command, restore_command, ssl_passphrase_command,
## log_destination, event_source
##
## IMPORTANT: Do NOT override CloudNativePG-managed parameters:
## archive_mode, primary_conninfo, wal_level, max_replication_slots
##
## NOTE: Some parameters require pod restart (not just reload):
## shared_buffers, max_connections, max_prepared_transactions,
## wal_buffers, max_wal_senders
##
## @section Quorum-based synchronous replication

View file

@ -1,42 +0,0 @@
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: vm-not-running-alert
spec:
groups:
- name: kubevirt-alerts
rules:
- alert: VMNotRunningFor10Minutes
expr: |
max_over_time(
kubevirt_vm_info{
status!="running",
exported_namespace=~".+",
name=~".+"
}[10m]
) == 1
labels:
severity: critical
namespace: "{{ $labels.exported_namespace }}"
annotations:
summary: "VM '{{ $labels.name }}' in namespace '{{ $labels.exported_namespace }}' is not Running for more than 10 minutes"
description: >
The VM '{{ $labels.name }}' in namespace '{{ $labels.exported_namespace }}' has been in a non-Running state
for more than 10 minutes. Please investigate the issue.
- alert: VMINotRunningFor10Minutes
expr: |
max_over_time(
kubevirt_vmi_info{
phase!="running",
exported_namespace=~".+",
name=~".+"
}[10m]
) == 1
labels:
severity: critical
namespace: "{{ $labels.exported_namespace }}"
annotations:
summary: "VMI '{{ $labels.name }}' in namespace '{{ $labels.exported_namespace }}' is not Running for more than 10 minutes"
description: >
The VM '{{ $labels.name }}' in namespace '{{ $labels.exported_namespace }}' has been in a non-Running state
for more than 10 minutes. Please investigate the issue.

View file

@ -0,0 +1,38 @@
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: vm-not-running-alert
spec:
groups:
- name: kubevirt-alerts
rules:
- alert: VMNotRunningFor10Minutes
expr: |
max_over_time(
kubevirt_vm_info{
status!="running",
exported_namespace=~".+",
name=~".+"
}[10m]
) == 1
labels:
severity: critical
namespace: '{{ "{{" }} $labels.exported_namespace {{ "}}" }}'
annotations:
summary: 'VM {{ "{{" }} $labels.name {{ "}}" }} in namespace {{ "{{" }} $labels.exported_namespace {{ "}}" }} is not Running for more than 10 minutes'
description: 'The VM {{ "{{" }} $labels.name {{ "}}" }} in namespace {{ "{{" }} $labels.exported_namespace {{ "}}" }} has been in a non-Running state for more than 10 minutes. Please investigate the issue.'
- alert: VMINotRunningFor10Minutes
expr: |
max_over_time(
kubevirt_vmi_info{
phase!="running",
exported_namespace=~".+",
name=~".+"
}[10m]
) == 1
labels:
severity: critical
namespace: '{{ "{{" }} $labels.exported_namespace {{ "}}" }}'
annotations:
summary: 'VMI {{ "{{" }} $labels.name {{ "}}" }} in namespace {{ "{{" }} $labels.exported_namespace {{ "}}" }} is not Running for more than 10 minutes'
description: 'The VMI {{ "{{" }} $labels.name {{ "}}" }} in namespace {{ "{{" }} $labels.exported_namespace {{ "}}" }} has been in a non-Running state for more than 10 minutes. Please investigate the issue.'

File diff suppressed because one or more lines are too long