Skip to content

feat: add default CA bundle for clusters (#29170) - #29303

Open
KHARSHAVARDHAN-eng wants to merge 1 commit into
argoproj:masterfrom
KHARSHAVARDHAN-eng:feat/29170-default-ca-bundle
Open

feat: add default CA bundle for clusters (#29170)#29303
KHARSHAVARDHAN-eng wants to merge 1 commit into
argoproj:masterfrom
KHARSHAVARDHAN-eng:feat/29170-default-ca-bundle

Conversation

@KHARSHAVARDHAN-eng

Copy link
Copy Markdown
Contributor

Resolves #29170

Summary

  • Adds a dedicated argocd-default-ca-cm ConfigMap with the ca.crt key for a shared default CA bundle.
  • Uses the cluster's tlsClientConfig.caData when provided.
  • Falls back to the default CA bundle only when the cluster does not provide caData.
  • Preserves existing behavior when neither is configured; CA bundles are not merged.
  • Keeps the default CA bundle runtime-only and includes the ConfigMap in admin backup/restore handling.
  • Adds regression tests and documentation for the new fallback behavior.

Testing

  • go test ./pkg/apis/application/v1alpha1/... — PASS
  • go test ./util/settings/... — PASS
  • go test -v ./util/db -run "TestCluster|TestDB_GetCluster" — PASS
  • go test ./cmd/argocd/commands/admin/... — PASS
  • gofmt — PASS
  • git diff --check — PASS
  • go test -v ./util/db -run Test_ListConfiguredGPGPublicKeys — could not run locally because gpg is not installed

@KHARSHAVARDHAN-eng
KHARSHAVARDHAN-eng requested review from a team as code owners August 21, 2026 07:49
@bunnyshell

bunnyshell Bot commented Aug 21, 2026

Copy link
Copy Markdown

❗ Preview Environment deployment failed on Bunnyshell

See: Environment Details | Pipeline Logs

Available commands (reply to this comment):

  • 🚀 /bns:deploy to redeploy the environment
  • /bns:delete to remove the environment

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

feat: add default CA bundle fallback for cluster connections (#29170)

✨ Enhancement 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Introduces argocd-default-ca-cm ConfigMap holding a shared default CA bundle (ca.crt).
• Cluster's own tlsClientConfig.caData still takes precedence; default CA bundle is used only as a
 fallback when caData is empty, without merging.
• Wires the new ConfigMap through settings manager, DB layer, REST config construction, admin
 backup/restore, and generated deepcopy code.
• Adds regression tests and operator-manual documentation describing the fallback and precedence
 rules.
Diagram

graph TD
  CM[(argocd-default-ca-cm)] --> SM["SettingsManager.GetDefaultCABundle"] --> DB["db.attachDefaultCABundle"] --> CL["Cluster.DefaultCABundle"] --> RC{"caData present?"}
  RC -->|yes| USE_CLUSTER["Use cluster CAData"]
  RC -->|no| USE_DEFAULT["Use default CA bundle"]
  USE_CLUSTER --> REST["RawRestConfig / REST Client"]
  USE_DEFAULT --> REST
  BK["admin backup/restore"] --> CM
  subgraph Legend
    direction LR
    _db[(Database)] ~~~ _svc([Service]) ~~~ _dec{Decision}
  end
Loading
High-Level Assessment

Using a dedicated ConfigMap with an optional per-call fallback parameter (rather than merging CA data or overloading an existing ConfigMap) keeps the change backward-compatible and low-risk. This is the straightforward, idiomatic approach for Argo CD's existing settings/ConfigMap patterns.

Files changed (17) +439 / -6

Enhancement (8) +71 / -6
admin.goRecognize argocd-default-ca-cm as a well-known ConfigMap +1/-1

Recognize argocd-default-ca-cm as a well-known ConfigMap

• Adds ArgoCDDefaultCAConfigMapName to the list of recognized Argo CD ConfigMaps for admin tooling.

cmd/argocd/commands/admin/admin.go

backup.goInclude default CA ConfigMap in admin export/backup +6/-0

Include default CA ConfigMap in admin export/backup

• Exports the new argocd-default-ca-cm ConfigMap during backup, tolerating the case where it does not exist.

cmd/argocd/commands/admin/backup.go

common.goAdd ArgoCDDefaultCAConfigMapName constant +2/-0

Add ArgoCDDefaultCAConfigMapName constant

• Defines the new argocd-default-ca-cm ConfigMap name constant used across the codebase.

common/common.go

types.goAdd DefaultCABundle field and fallback logic in RawRestConfig/RESTConfig +16/-4

Add DefaultCABundle field and fallback logic in RawRestConfig/RESTConfig

• Adds a runtime-only DefaultCABundle field to Cluster and updates RawRestConfig/RESTConfig to accept an optional default CA bundle, using it only when cluster caData is empty.

pkg/apis/application/v1alpha1/types.go

zz_generated.deepcopy.goUpdate generated deepcopy for new DefaultCABundle field +4/-0

Update generated deepcopy for new DefaultCABundle field

• Regenerates DeepCopyInto for Cluster to clone the new DefaultCABundle byte slice.

pkg/apis/application/v1alpha1/zz_generated.deepcopy.go

cluster.goAttach default CA bundle to clusters retrieved from DB +24/-1

Attach default CA bundle to clusters retrieved from DB

• Adds attachDefaultCABundle helper invoked from getLocalCluster, ListClusters, GetCluster, and GetProjectClusters, plus a new GetDefaultCABundle DB method.

util/db/cluster.go

db.goAdd GetDefaultCABundle to ArgoDB interface +2/-0

Add GetDefaultCABundle to ArgoDB interface

• Extends the ArgoDB interface with a GetDefaultCABundle method.

util/db/db.go

settings.goAdd GetDefaultCABundle to SettingsManager +16/-0

Add GetDefaultCABundle to SettingsManager

• Reads the ca.crt key from argocd-default-ca-cm, returning nil without error if the ConfigMap or key is missing or blank.

util/settings/settings.go

Tests (4) +317 / -0
types_test.goAdd regression tests for CA bundle fallback precedence +135/-0

Add regression tests for CA bundle fallback precedence

• Adds tests covering cluster CAData precedence, fallback to default CA bundle, no-merge behavior, and that DefaultCABundle is excluded from JSON serialization.

pkg/apis/application/v1alpha1/types_test.go

cluster_test.goAdd tests for default CA bundle attachment in DB layer +108/-0

Add tests for default CA bundle attachment in DB layer

• Adds tests verifying GetCluster, ListClusters, and the local cluster correctly attach the default CA bundle, and that behavior is safe when the ConfigMap is absent.

util/db/cluster_test.go

ArgoDB.goRegenerate ArgoDB mock with GetDefaultCABundle +20/-0

Regenerate ArgoDB mock with GetDefaultCABundle

• Adds the generated mock implementation for the new GetDefaultCABundle interface method.

util/db/mocks/ArgoDB.go

settings_test.goAdd tests for SettingsManager.GetDefaultCABundle +54/-0

Add tests for SettingsManager.GetDefaultCABundle

• Covers missing ConfigMap, missing/blank ca.crt key, and successful retrieval of the CA bundle.

util/settings/settings_test.go

Documentation (3) +43 / -0
argocd-default-ca-cm-yaml.mdAdd doc page embedding default CA ConfigMap example +7/-0

Add doc page embedding default CA ConfigMap example

• New documentation page including the example argocd-default-ca-cm.yaml manifest.

docs/operator-manual/argocd-default-ca-cm-yaml.md

argocd-default-ca-cm.yamlAdd example default CA bundle ConfigMap manifest +11/-0

Add example default CA bundle ConfigMap manifest

• New example YAML illustrating the structure of the argocd-default-ca-cm ConfigMap with a ca.crt key.

docs/operator-manual/argocd-default-ca-cm.yaml

declarative-setup.mdDocument default CA bundle ConfigMap and fallback rules +25/-0

Document default CA bundle ConfigMap and fallback rules

• Adds the new ConfigMap to the resource table and documents fallback/precedence rules for CA data.

docs/operator-manual/declarative-setup.md

Other (2) +8 / -0
argocd-default-ca-cm.yamlAdd base manifest for argocd-default-ca-cm ConfigMap +7/-0

Add base manifest for argocd-default-ca-cm ConfigMap

• New Kustomize base resource defining the empty argocd-default-ca-cm ConfigMap with standard labels.

manifests/base/config/argocd-default-ca-cm.yaml

kustomization.yamlRegister argocd-default-ca-cm.yaml as a kustomize resource +1/-0

Register argocd-default-ca-cm.yaml as a kustomize resource

• Adds the new ConfigMap manifest to the base kustomization resources list.

manifests/base/config/kustomization.yaml

@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Repeated CA lookup 🐞 Bug ➹ Performance
Description
ListClusters calls attachDefaultCABundle once per returned cluster, and attachDefaultCABundle
re-reads the default CA ConfigMap each time; this adds avoidable O(n) cache lookups/locks and
increases latency as cluster count grows.
Code

util/db/cluster.go[R110-112]

+	for i := range clusterList.Items {
+		db.attachDefaultCABundle(&clusterList.Items[i])
+	}
Evidence
The PR introduces a per-cluster loop that calls a helper, and that helper performs the ConfigMap
lookup each time; together these changes produce N redundant CA bundle reads for N clusters.

util/db/cluster.go[67-76]
util/db/cluster.go[110-112]
util/settings/settings.go[1981-1994]
util/settings/settings.go[803-819]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ListClusters()` loops over all clusters and calls `attachDefaultCABundle()` for each item, but `attachDefaultCABundle()` reads the default CA bundle from the settings manager on every call. This creates unnecessary repeated cache lookups/locking proportional to the number of clusters.

### Issue Context
- `attachDefaultCABundle()` always calls `db.settingsMgr.GetDefaultCABundle()`.
- `ListClusters()` currently calls `attachDefaultCABundle(&clusterList.Items[i])` inside a loop.

### Fix Focus Areas
- util/db/cluster.go[67-76]
- util/db/cluster.go[110-112]

### Suggested approach
- Refactor so the default CA bundle is fetched once per `ListClusters()` call (or once per request) and then applied to all returned clusters.
 - Option A: make `attachDefaultCABundle` accept a `defaultCA []byte` argument (already resolved) and only do assignment.
 - Option B: in `ListClusters`, call `GetDefaultCABundle()` once, then assign to each cluster.
- If you keep `attachDefaultCABundle(clusters ...*Cluster)`, call it once with all cluster pointers rather than once per cluster.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Swallowed CA read errors 🐞 Bug ◔ Observability
Description
attachDefaultCABundle drops any non-nil error from SettingsManager.GetDefaultCABundle, so failures
to read the default CA ConfigMap (e.g., cache sync/RBAC/API errors) become silent and later TLS
connection failures lose their root cause.
Code

util/db/cluster.go[R68-70]

+	defaultCA, err := db.settingsMgr.GetDefaultCABundle()
+	if err != nil || len(defaultCA) == 0 {
+		return
Evidence
The new helper explicitly returns on any error, while the underlying settings call only suppresses
NotFound; this means unexpected errors are silently ignored.

util/db/cluster.go[67-70]
util/settings/settings.go[1983-1990]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`attachDefaultCABundle()` returns early on any error from `GetDefaultCABundle()` without logging or surfacing the error. This can mask real operational problems (RBAC, API failures, informer sync issues) and makes subsequent TLS errors harder to diagnose.

### Issue Context
- `SettingsManager.GetDefaultCABundle()` returns an error for non-NotFound failures.
- `attachDefaultCABundle()` currently treats *all* errors as “ignore and proceed”.

### Fix Focus Areas
- util/db/cluster.go[67-70]
- util/settings/settings.go[1983-1990]

### Suggested approach
- At minimum: log a warning when `GetDefaultCABundle()` returns a non-NotFound error.
- Preferably: return `(bool, error)` or `error` from `attachDefaultCABundle()` so callers can decide whether to fail the operation (e.g., `GetCluster`) or continue with a warning (e.g., `ListClusters`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Misleading CA comment 🐞 Bug ⚙ Maintainability
Description
The GetDefaultCABundle comment says it returns an empty byte slice when missing, but the
implementation returns nil; this contradicts the documented behavior and can mislead callers about
nil-handling.
Code

util/settings/settings.go[R1981-1983]

+// GetDefaultCABundle returns the default CA bundle byte slice stored in argocd-default-ca-cm (key: ca.crt).
+// If the ConfigMap or key does not exist, an empty byte slice is returned without error.
+func (mgr *SettingsManager) GetDefaultCABundle() ([]byte, error) {
Evidence
The comment promises an empty slice, but both NotFound and missing/blank key paths return nil, and
the function has no branch that returns an empty-but-non-nil slice.

util/settings/settings.go[1981-1994]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The doc comment for `GetDefaultCABundle()` says the function returns an “empty byte slice” when missing, but the implementation returns `nil` in those cases.

### Issue Context
Tests also assert `nil`, so either the comment or the behavior needs to change to match.

### Fix Focus Areas
- util/settings/settings.go[1981-1994]

### Suggested approach
- Update the comment to say it returns `nil, nil` when the ConfigMap/key is missing or empty (or change the implementation to return `[]byte{}` consistently, but then update callers/tests accordingly).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can tweak Display preferences with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread util/db/cluster.go
Comment on lines +110 to +112
for i := range clusterList.Items {
db.attachDefaultCABundle(&clusterList.Items[i])
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Repeated ca lookup 🐞 Bug ➹ Performance

ListClusters calls attachDefaultCABundle once per returned cluster, and attachDefaultCABundle
re-reads the default CA ConfigMap each time; this adds avoidable O(n) cache lookups/locks and
increases latency as cluster count grows.
Agent Prompt
### Issue description
`ListClusters()` loops over all clusters and calls `attachDefaultCABundle()` for each item, but `attachDefaultCABundle()` reads the default CA bundle from the settings manager on every call. This creates unnecessary repeated cache lookups/locking proportional to the number of clusters.

### Issue Context
- `attachDefaultCABundle()` always calls `db.settingsMgr.GetDefaultCABundle()`.
- `ListClusters()` currently calls `attachDefaultCABundle(&clusterList.Items[i])` inside a loop.

### Fix Focus Areas
- util/db/cluster.go[67-76]
- util/db/cluster.go[110-112]

### Suggested approach
- Refactor so the default CA bundle is fetched once per `ListClusters()` call (or once per request) and then applied to all returned clusters.
  - Option A: make `attachDefaultCABundle` accept a `defaultCA []byte` argument (already resolved) and only do assignment.
  - Option B: in `ListClusters`, call `GetDefaultCABundle()` once, then assign to each cluster.
- If you keep `attachDefaultCABundle(clusters ...*Cluster)`, call it once with all cluster pointers rather than once per cluster.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread util/db/cluster.go
Comment on lines +68 to +70
defaultCA, err := db.settingsMgr.GetDefaultCABundle()
if err != nil || len(defaultCA) == 0 {
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Swallowed ca read errors 🐞 Bug ◔ Observability

attachDefaultCABundle drops any non-nil error from SettingsManager.GetDefaultCABundle, so failures
to read the default CA ConfigMap (e.g., cache sync/RBAC/API errors) become silent and later TLS
connection failures lose their root cause.
Agent Prompt
### Issue description
`attachDefaultCABundle()` returns early on any error from `GetDefaultCABundle()` without logging or surfacing the error. This can mask real operational problems (RBAC, API failures, informer sync issues) and makes subsequent TLS errors harder to diagnose.

### Issue Context
- `SettingsManager.GetDefaultCABundle()` returns an error for non-NotFound failures.
- `attachDefaultCABundle()` currently treats *all* errors as “ignore and proceed”.

### Fix Focus Areas
- util/db/cluster.go[67-70]
- util/settings/settings.go[1983-1990]

### Suggested approach
- At minimum: log a warning when `GetDefaultCABundle()` returns a non-NotFound error.
- Preferably: return `(bool, error)` or `error` from `attachDefaultCABundle()` so callers can decide whether to fail the operation (e.g., `GetCluster`) or continue with a warning (e.g., `ListClusters`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread util/settings/settings.go
Comment on lines +1981 to +1983
// GetDefaultCABundle returns the default CA bundle byte slice stored in argocd-default-ca-cm (key: ca.crt).
// If the ConfigMap or key does not exist, an empty byte slice is returned without error.
func (mgr *SettingsManager) GetDefaultCABundle() ([]byte, error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

3. Misleading ca comment 🐞 Bug ⚙ Maintainability

The GetDefaultCABundle comment says it returns an empty byte slice when missing, but the
implementation returns nil; this contradicts the documented behavior and can mislead callers about
nil-handling.
Agent Prompt
### Issue description
The doc comment for `GetDefaultCABundle()` says the function returns an “empty byte slice” when missing, but the implementation returns `nil` in those cases.

### Issue Context
Tests also assert `nil`, so either the comment or the behavior needs to change to match.

### Fix Focus Areas
- util/settings/settings.go[1981-1994]

### Suggested approach
- Update the comment to say it returns `nil, nil` when the ConfigMap/key is missing or empty (or change the implementation to return `[]byte{}` consistently, but then update callers/tests accordingly).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@chengfang

Copy link
Copy Markdown
Contributor

The new configmap name argocd-default-ca-cm is quite generic and users may think it's for all tls connections. We may need more descriptive name, say, argocd-cluster-ca-cm or the like.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add cluster.defaultCABundle setting for default CA bundle on cluster connections

2 participants