Skip to content
Open
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
23 changes: 23 additions & 0 deletions api/v1alpha1/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,31 @@ type ProxyConfig struct {
// +kubebuilder:validation:MaxLength:=4096
// +optional
NoProxy string `json:"noProxy,omitempty"`

// networkPolicyAllowProxyEgressAll controls whether the operator automatically creates a NetworkPolicy
// that allows ESO pods to reach the proxy server configured on the cluster.
// Managed (default): the operator creates and maintains the eso-sys-proxy-egress-core NetworkPolicy
// whenever an effective proxy is configured via getProxyConfiguration().
// Unmanaged: the operator does not create this policy; administrators are responsible for
// defining their own egress rules for proxy traffic.
// This field has no effect on clusters without a proxy configuration.
// +kubebuilder:validation:Enum:=Managed;Unmanaged
// +kubebuilder:default:=Managed
// +optional
NetworkPolicyAllowProxyEgressAll NetworkPolicyManagementMode `json:"networkPolicyAllowProxyEgressAll,omitempty"`
}

// NetworkPolicyManagementMode indicates whether the operator manages a NetworkPolicy automatically.
type NetworkPolicyManagementMode string

const (
// Managed indicates the operator creates and maintains the NetworkPolicy automatically.
Managed NetworkPolicyManagementMode = "Managed"

// Unmanaged indicates the operator does not create the NetworkPolicy; the administrator manages it.
Unmanaged NetworkPolicyManagementMode = "Unmanaged"
)

// Mode indicates the operational state of the optional features.
type Mode string

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,96 @@ tests:
injectAnnotations: "false"
certificateDuration: "8760h"
certificateRenewBefore: "30m"
- name: Should accept networkPolicyAllowProxyEgressAll set to Managed (default)
resourceName: cluster
initial: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Managed
expected: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
logLevel: 1
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Managed
- name: Should accept networkPolicyAllowProxyEgressAll set to Unmanaged
resourceName: cluster
initial: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Unmanaged
expected: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
logLevel: 1
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Unmanaged
- name: Should default networkPolicyAllowProxyEgressAll to Managed when proxy is set without the field
resourceName: cluster
initial: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
proxy:
httpProxy: "http://proxy.example.com:3128"
expected: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
logLevel: 1
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Managed
- name: Should fail with invalid value for networkPolicyAllowProxyEgressAll
resourceName: cluster
initial: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Enabled
expectedError: "spec.appConfig.proxy.networkPolicyAllowProxyEgressAll: Unsupported value: \"Enabled\": supported values: \"Managed\", \"Unmanaged\""
- name: Should accept proxy config with all fields including networkPolicyAllowProxyEgressAll
resourceName: cluster
initial: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
proxy:
httpProxy: "http://proxy.example.com:3128"
httpsProxy: "https://proxy.example.com:3128"
noProxy: "localhost,127.0.0.1,.cluster.local"
networkPolicyAllowProxyEgressAll: Unmanaged
expected: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
logLevel: 1
proxy:
httpProxy: "http://proxy.example.com:3128"
httpsProxy: "https://proxy.example.com:3128"
noProxy: "localhost,127.0.0.1,.cluster.local"
networkPolicyAllowProxyEgressAll: Unmanaged
onUpdate:
- name: Should be able to update labels in controller config
resourceName: cluster
Expand Down Expand Up @@ -1726,3 +1816,30 @@ tests:
- to:
- ipBlock:
cidr: 172.16.0.0/12
- name: Should be able to update networkPolicyAllowProxyEgressAll from Managed to Unmanaged
resourceName: cluster
initial: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Managed
updated: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Unmanaged
expected: |
apiVersion: operator.openshift.io/v1alpha1
kind: ExternalSecretsConfig
spec:
appConfig:
logLevel: 1
proxy:
httpProxy: "http://proxy.example.com:3128"
networkPolicyAllowProxyEgressAll: Unmanaged
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ tests:
httpProxy: "http://proxy.example.com:8080"
httpsProxy: "https://proxy.example.com:8443"
noProxy: "localhost,127.0.0.1,.local"
networkPolicyAllowProxyEgressAll: Managed
- name: Should fail to create with invalid singleton name
resourceName: invalid-name
initial: |
Expand Down Expand Up @@ -182,6 +183,7 @@ tests:
logLevel: 1
proxy:
httpProxy: "http://proxy-url-at-exactly-two-thousand-and-forty-eight-characters-to-test-the-boundary-condition-where-we-want-to-ensure-that-urls-at-the-maximum-allowed-length-are-accepted-properly-by-the-validation-system-while-urls-that-exceed-this-limit-are-rejected-appropriately-which-is-important-for-maintaining-proper-validation-boundaries-in-production-systems-where-configuration-parameters-must-be-validated-correctly-to-prevent-system-failures-or-unexpected-behavior-that-could-impact-application-functionality-and-user-experience-in-various-deployment-environments-including-development-staging-and-production-kubernetes-clusters-running-across-different-cloud-providers-and-on-premises-infrastructure-where-proxy-configurations-are-commonly-used-for-network-security-and-compliance-requirements-that-organizations-need-to-meet-for-their-business-operations-and-regulatory-obligations-in-different-geographical-regions-around-the-world-where-various-network-policies-and-security-measures-are-implemented-to-protect-sensitive-data-and-ensure-proper-access-control-for-applications-and-services-that-handle-confidential-information-and-business-critical-processes-that-must-operate-reliably-and-securely-at-all-times-without-interruption-or-performance-degradation-that-could-affect-end-users-and-customers-who-depend-on-these-systems-for-their-daily-activities-and-business-needs-which-makes-proper-validation-of-configuration-parameters-like-proxy-urls-essential-for-maintaining-system-stability-and-security-in-production-environments-where-any-configuration-error-could-have-significant-consequences-for-business-continuity-and-customer-satisfaction-which-is-why-we-implement-comprehensive-boundary-testing-to-ensure-that-all-validation-rules-work-correctly-at-their-specified-limits-and-provide-clear-error-messages-when-those-limits-are-exceeded-by-user-configurations.example.com:8080"
networkPolicyAllowProxyEgressAll: Managed
- name: Should accept HTTPS proxy URL at maximum length boundary
resourceName: cluster
initial: |
Expand All @@ -200,6 +202,7 @@ tests:
logLevel: 1
proxy:
httpsProxy: "https://secure-proxy-url-at-exactly-two-thousand-and-forty-eight-characters-to-test-the-boundary-condition-where-we-want-to-ensure-that-urls-at-the-maximum-allowed-length-are-accepted-properly-by-the-validation-system-while-urls-that-exceed-this-limit-are-rejected-appropriately-which-is-important-for-maintaining-proper-validation-boundaries-in-production-systems-where-configuration-parameters-must-be-validated-correctly-to-prevent-system-failures-or-unexpected-behavior-that-could-impact-application-functionality-and-user-experience-in-various-deployment-environments-including-development-staging-and-production-kubernetes-clusters-running-across-different-cloud-providers-and-on-premises-infrastructure-where-proxy-configurations-are-commonly-used-for-network-security-and-compliance-requirements-that-organizations-need-to-meet-for-their-business-operations-and-regulatory-obligations-in-different-geographical-regions-around-the-world-where-various-network-policies-and-security-measures-are-implemented-to-protect-sensitive-data-and-ensure-proper-access-control-for-applications-and-services-that-handle-confidential-information-and-business-critical-processes-that-must-operate-reliably-and-securely-at-all-times-without-interruption-or-performance-degradation-that-could-affect-end-users-and-customers-who-depend-on-these-systems-for-their-daily-activities-and-business-needs-which-makes-proper-validation-of-configuration-parameters-like-proxy-urls-essential-for-maintaining-system-stability-and-security-in-production-environments-where-any-configuration-error-could-have-significant-consequences-for-business-continuity-and-customer-satisfaction-which-is-why-we-implement-comprehensive-boundary-testing-to-ensure-that-all-validation-rules-work-correctly-at-their-specified-limits-and-provide-clear-error-messages-when-those-limits-are-exceeded.example.com:8443"
networkPolicyAllowProxyEgressAll: Managed
- name: Should accept empty proxy configuration
resourceName: cluster
initial: |
Expand All @@ -221,6 +224,7 @@ tests:
httpProxy: ""
httpsProxy: ""
noProxy: ""
networkPolicyAllowProxyEgressAll: Managed
onUpdate:
- name: Should be able to add global config after creation
resourceName: cluster
Expand Down Expand Up @@ -338,6 +342,7 @@ tests:
httpProxy: "http://proxy.company.com:3128"
httpsProxy: "https://proxy.company.com:3128"
noProxy: "localhost,127.0.0.1,.company.com"
networkPolicyAllowProxyEgressAll: Managed
- name: Should be able to update node selector and tolerations
resourceName: cluster
initial: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ spec:
- networkpolicies
verbs:
- create
- delete
- get
- list
- update
Expand Down
14 changes: 14 additions & 0 deletions config/crd/bases/operator.openshift.io_externalsecretsconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,20 @@ spec:
maxLength: 2048
minLength: 0
type: string
networkPolicyAllowProxyEgressAll:
description: |-
networkPolicyAllowProxyEgressAll controls whether the operator automatically creates a NetworkPolicy
that allows ESO pods to reach the proxy server configured on the cluster.
Managed (default): the operator creates and maintains the eso-sys-proxy-egress-core NetworkPolicy
whenever an effective proxy is configured via getProxyConfiguration().
Unmanaged: the operator does not create this policy; administrators are responsible for
defining their own egress rules for proxy traffic.
This field has no effect on clusters without a proxy configuration.
default: Managed
enum:
- Managed
- Unmanaged
type: string
noProxy:
description: |-
noProxy is a comma-separated list of hostnames and/or CIDRs and/or IPs for which the proxy should not be used.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,20 @@ spec:
maxLength: 2048
minLength: 0
type: string
networkPolicyAllowProxyEgressAll:
description: |-
networkPolicyAllowProxyEgressAll controls whether the operator automatically creates a NetworkPolicy
that allows ESO pods to reach the proxy server configured on the cluster.
Managed (default): the operator creates and maintains the eso-sys-proxy-egress-core NetworkPolicy
whenever an effective proxy is configured via getProxyConfiguration().
Unmanaged: the operator does not create this policy; administrators are responsible for
defining their own egress rules for proxy traffic.
This field has no effect on clusters without a proxy configuration.
default: Managed
enum:
- Managed
- Unmanaged
type: string
noProxy:
description: |-
noProxy is a comma-separated list of hostnames and/or CIDRs and/or IPs for which the proxy should not be used.
Expand Down
1 change: 1 addition & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ rules:
- networkpolicies
verbs:
- create
- delete
- get
- list
- update
Expand Down
13 changes: 13 additions & 0 deletions pkg/controller/external_secrets/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ const (
// https://cs.opensource.google/go/go/+/refs/tags/go1.24.4:src/crypto/x509/root_linux.go;l=22
trustedCABundleMountPath = "/etc/pki/tls/certs"

// proxyEgressNetworkPolicyName is the name of the programmatically-built proxy egress NetworkPolicy.
// Created only when spec.appConfig.proxy.networkPolicyAllowProxyEgressAll is Managed (default)
// and an effective proxy is configured via getProxyConfiguration().
proxyEgressNetworkPolicyName = "eso-sys-proxy-egress-core"

// migrationCompleteAnnotation is set on ExternalSecretsConfig after legacy unprefixed
// NetworkPolicies have been pruned on the first reconcile under the new naming scheme.
migrationCompleteAnnotation = "operator.openshift.io/network-policy-migration-complete"

// userNetworkPolicyPrefix is prepended to user-defined NetworkPolicy names from spec.networkPolicies[].
// The user writes name: allow-external-secrets-egress; the K8s object is eso-user-allow-external-secrets-egress.
userNetworkPolicyPrefix = "eso-user-"
Comment on lines +78 to +80
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import re, pathlib
prefix = "eso-user-"
crd_path = "config/crd/bases/operator.openshift.io_externalsecretsconfigs.yaml"
text = pathlib.Path(crd_path).read_text()

m = re.search(r'networkPolicies:\n(?:.*\n)*?\s+name:\n(?:.*\n)*?\s+maxLength:\s*(\d+)', text)
crd_max = int(m.group(1)) if m else None

print("CRD maxLength for user policy name:", crd_max)
print("Prefix length:", len(prefix))
print("Max safe user name length with prefix:", 253 - len(prefix))
if crd_max is not None and crd_max > 253 - len(prefix):
    print("MISMATCH: prefixed name can exceed Kubernetes metadata.name limit (253).")
PY

Repository: openshift/external-secrets-operator

Length of output: 252


🏁 Script executed:

# 1. Find where userNetworkPolicyPrefix is used
rg "userNetworkPolicyPrefix" -A 5 -B 2

Repository: openshift/external-secrets-operator

Length of output: 7102


🏁 Script executed:

# 2. Search for NetworkPolicy creation/name generation logic
rg -i "networkpolicy" --type go -A 3 -B 1 | head -100

Repository: openshift/external-secrets-operator

Length of output: 11502


🏁 Script executed:

# 3. Check CRD validation for networkPolicies more thoroughly
cat config/crd/bases/operator.openshift.io_externalsecretsconfigs.yaml | grep -A 30 "networkPolicies:" | head -50

Repository: openshift/external-secrets-operator

Length of output: 2202


🏁 Script executed:

# 4. Look for any existing name validation or truncation logic
rg "maxUserNameLen|truncat|255|253" --type go

Repository: openshift/external-secrets-operator

Length of output: 50392


🏁 Script executed:

# Get the buildNetworkPolicyFromConfig implementation
sed -n '/func (r \*Reconciler) buildNetworkPolicyFromConfig/,/^func /p' pkg/controller/external_secrets/networkpolicy.go | head -80

Repository: openshift/external-secrets-operator

Length of output: 1196


🏁 Script executed:

# Check if there's any name length validation in the codebase
rg "len.*Name|Name.*len|maxLength|validate.*name" pkg/controller/external_secrets --type go -A 2 -B 2

Repository: openshift/external-secrets-operator

Length of output: 1356


🏁 Script executed:

# Get the full networkPolicies CRD definition to see if there's validation we missed
cat config/crd/bases/operator.openshift.io_externalsecretsconfigs.yaml | grep -A 80 "networkPolicies:" | grep -A 60 "name:"

Repository: openshift/external-secrets-operator

Length of output: 61


🏁 Script executed:

# Search for the name field in networkPolicies section of the CRD
grep -A 150 "networkPolicies:" config/crd/bases/operator.openshift.io_externalsecretsconfigs.yaml | grep -A 30 "name:"

Repository: openshift/external-secrets-operator

Length of output: 61


🏁 Script executed:

# Check if there's any existing validation or constraints on the combined name
rg "userNetworkPolicyPrefix.*Name|Name.*userNetworkPolicyPrefix" pkg/controller/external_secrets --type go -B 5 -A 5

Repository: openshift/external-secrets-operator

Length of output: 5934


🏁 Script executed:

# Look for any validation functions or checks that might constrain names
rg "validation|Validate|validate" pkg/controller/external_secrets/networkpolicy.go --type go

Repository: openshift/external-secrets-operator

Length of output: 61


🏁 Script executed:

# Read the CRD file directly and find the networkPolicies section
cat config/crd/bases/operator.openshift.io_externalsecretsconfigs.yaml | head -500 | tail -100

Repository: openshift/external-secrets-operator

Length of output: 7322


🏁 Script executed:

# Try to find the exact maxLength constraint in the CRD
rg "maxLength" config/crd/bases/operator.openshift.io_externalsecretsconfigs.yaml -B 3 -A 1

Repository: openshift/external-secrets-operator

Length of output: 3513


🏁 Script executed:

# Check the operator API types to see the NetworkPolicy struct definition
find . -name "*.go" -path "*/api/*" -o -name "types.go" | xargs rg "type NetworkPolicy struct" -A 15 2>/dev/null | head -50

Repository: openshift/external-secrets-operator

Length of output: 4758


Prefixing user NetworkPolicy names exceeds Kubernetes name limits.

The CRD allows user-provided policy names up to 253 characters (api/v1alpha1/external_secrets_config_types.go), but buildNetworkPolicyFromConfig directly concatenates the 9-character prefix without validation. This creates names up to 262 characters, exceeding Kubernetes' DNS-1123 subdomain limit of 253 characters, causing NetworkPolicy creation to fail at reconcile time.

Enforce a maximum user name length of 244 characters (253 - 9 for prefix) and align CRD validation accordingly. Alternatively, apply deterministic truncation or hashing before creating the NetworkPolicy object.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/external_secrets/constants.go` around lines 78 - 80, The code
concatenates userNetworkPolicyPrefix ("eso-user-") to user-provided names in
buildNetworkPolicyFromConfig which can exceed Kubernetes' 253-char DNS-1123
subdomain limit; enforce a max user name length of 244 characters (253 -
len("eso-user-")) or apply deterministic truncation/hashing before
concatenation. Update buildNetworkPolicyFromConfig to validate the incoming name
length (or truncate/hash it) and produce a final name <=253 chars, and align the
CRD validation (external_secrets_config_types.go) to cap the user-provided name
to 244 characters so reconcilers never try to create too-long NetworkPolicy
names.


// Proxy environment variable names (uppercase).
httpProxyEnvVar = "HTTP_PROXY"
httpsProxyEnvVar = "HTTPS_PROXY"
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/external_secrets/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ type Reconciler struct {
// +kubebuilder:rbac:groups="",resources=events;secrets;services;serviceaccounts,verbs=get;list;watch;create;update;delete;patch
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificates;clusterissuers;issuers,verbs=get;list;watch;create;update
// +kubebuilder:rbac:groups=networking.k8s.io,resources=networkpolicies,verbs=get;list;watch;create;update
// +kubebuilder:rbac:groups=networking.k8s.io,resources=networkpolicies,verbs=get;list;watch;create;update;delete
// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;update;patch

// +kubebuilder:rbac:groups="",resources=endpoints,verbs=get;list;watch
Expand Down
Loading