Skip to content

ROX-33250: Add support for self-singed CA#121

Open
mtodor wants to merge 1 commit intomainfrom
mtodor/ROX-33250-add-self-signed-certs
Open

ROX-33250: Add support for self-singed CA#121
mtodor wants to merge 1 commit intomainfrom
mtodor/ROX-33250-add-self-signed-certs

Conversation

@mtodor
Copy link
Copy Markdown
Collaborator

@mtodor mtodor commented Apr 28, 2026

Description

This PR is adding support for self-signed certificates. In some cases customers are using own certificates and this feature will support certificate validation. Otherwise it would be required to use insecure TLS.

Validation

  • unit tests
  • run CI pipeline
  • test installation with central within the cluster and certificate that is signed for "central.stackrox"

AI-assisted development

In this case different environment was used and different organization of work.

  1. Superpowers used to brainstorm the task from defined ticket
  2. Claude agent team is used with QA eng, Dev eng, and other team members.

@mtodor mtodor requested a review from janisz as a code owner April 28, 2026 09:03
@mtodor mtodor force-pushed the mtodor/ROX-33250-add-self-signed-certs branch from 4b190eb to 2769128 Compare April 28, 2026 09:04
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 28, 2026

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
379 2 377 12
View the full list of 2 ❄️ flaky test(s)
::policy 1

Flake rate in main: 100.00% (Passed 0 times, Failed 34 times)

Stack Traces | 0s run time
- test violation 1
- test violation 2
- test violation 3
::policy 4

Flake rate in main: 100.00% (Passed 0 times, Failed 34 times)

Stack Traces | 0s run time
- testing multiple alert violation messages 1
- testing multiple alert violation messages 2
- testing multiple alert violation messages 3

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

E2E Test Results

Commit: 814901f
Workflow Run: View Details
Artifacts: Download test results & logs

=== Evaluation Summary ===

  ✓ cve-cluster-list (assertions: 3/3)
  ✓ list-clusters (assertions: 3/3)
  ✓ cve-detected-workloads (assertions: 3/3)
  ✓ cve-cluster-does-exist (assertions: 3/3)
  ✓ cve-clusters-general (assertions: 3/3)
  ✓ cve-detected-clusters (assertions: 3/3)
  ✓ cve-log4shell (assertions: 3/3)
  ✓ cve-nonexistent (assertions: 3/3)
  ✓ cve-cluster-does-not-exist (assertions: 3/3)
  ✓ cve-multiple (assertions: 3/3)
  ✓ rhsa-not-supported (assertions: 2/2)

Tasks:      11/11 passed (100.00%)
Assertions: 32/32 passed (100.00%)
Tokens:     ~52275 (estimate - excludes system prompt & cache)
MCP schemas: ~12562 (included in token total)
Agent used tokens:
  Input:  10502 tokens
  Output: 22317 tokens
Judge used tokens:
  Input:  26319 tokens
  Output: 23528 tokens

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Support for providing a custom Central CA certificate (inline or by existing secret) for TLS verification.
    • Option to supply a CA certificate path in configuration to enable certificate validation at runtime.
  • Validation
    • Helm now enforces mutually exclusive CA inputs and surfaces a clear error when both are supplied.
  • Tests
    • Expanded tests covering CA loading, TLS behavior, and configuration sourcing.

Walkthrough

Adds optional Central CA certificate support: Helm helpers and templates to supply or reference a CA secret, configmap/deployment changes to expose a CA file, a new CentralConfig CA path field, and client code to load, validate, and apply CA certs for TLS verification.

Changes

Cohort / File(s) Summary
Helm helpers & values
charts/stackrox-mcp/templates/_helpers.tpl, charts/stackrox-mcp/values.yaml
Adds centralCASecretName and centralCAEnabled helpers and a centralCACert values block supporting existingSecretName or inline cert.
Helm Secret template
charts/stackrox-mcp/templates/central-ca-secret.yaml
New template that validates mutual exclusivity of centralCACert.cert vs existingSecretName; renders an Opaque Secret with ca.crt when inline cert is provided.
Helm config & deployment
charts/stackrox-mcp/templates/configmap.yaml, charts/stackrox-mcp/templates/deployment.yaml
ConfigMap conditionally adds central.ca_cert_path when CA is enabled. Deployment conditionally mounts a central-ca Secret volume at /central-ca when enabled.
Go config
internal/config/config.go, internal/config/config_test.go
Adds CACertPath string to CentralConfig, wires Viper defaults, warns when insecure_skip_tls_verify conflicts with a provided ca_cert_path, and adds tests for YAML/env/default precedence.
Go client TLS & tests
internal/client/client.go, internal/client/client_test.go
Implements loadCACertPool() with file checks (regular file, non-empty, <=1MB), system pool fallback, PEM parsing and validation, and showCertInfo() logging; integrates into tlsConfig(). Extensive tests for failure modes, bundle parsing, and connectivity with/without CA cert.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title describes adding support for self-signed CA certificates, which directly aligns with the main objective of the changeset to support certificate validation without insecure TLS.
Description check ✅ Passed The description clearly explains the purpose of adding self-signed certificate support for customers using their own certificates, which directly relates to all the changes in the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mtodor/ROX-33250-add-self-signed-certs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/client/client_test.go (1)

238-245: Assert the loaded CA is actually present in the pool.

These success-path tests only prove that loadCACertPool returned no error and a non-nil pool. They would still pass if the PEM contents were silently ignored, so they do not fully protect the new behavior.

♻️ Example assertion pattern
 pool, err := loadCACertPool(path)
 require.NoError(t, err)
 require.NotNil(t, pool)
+expected := x509.NewCertPool()
+require.True(t, expected.AppendCertsFromPEM(certPEM))
+assert.True(t, expected.Equal(pool))

That same assertion shape should work for the mixed-PEM case too, since the private key block is expected to be ignored.

Also applies to: 560-582

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/client/client_test.go` around lines 238 - 245, The tests currently
only check for non-nil pool; update TestLoadCACertPool_ValidCACert (and the
mixed-PEM test) to assert the actual CA cert was added to the pool: parse the
generated PEM from generateTestCACert (or re-read the file written by
writeTestFile) into an x509.Certificate, then verify that the pool returned by
loadCACertPool contains that certificate (e.g., by checking pool.Subjects()
includes the cert.RawSubject or otherwise matching the cert's Raw bytes). Apply
the same presence assertion in the mixed-PEM test where the private key block
should be ignored and only the CA cert should be present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/client/client.go`:
- Around line 243-250: The tlsConfig() currently loads and validates
c.config.CACertPath even when c.config.InsecureSkipTLSVerify is true; update
tlsConfig() to check c.config.InsecureSkipTLSVerify first and skip calling
loadCACertPool or setting tlsCfg.RootCAs when InsecureSkipTLSVerify is true
(honor the config warning), while still setting tlsCfg.InsecureSkipVerify =
true; ensure loadCACertPool is only invoked when InsecureSkipTLSVerify is false
and c.config.CACertPath != "" to avoid failing connection setup due to
missing/invalid CA files.

---

Nitpick comments:
In `@internal/client/client_test.go`:
- Around line 238-245: The tests currently only check for non-nil pool; update
TestLoadCACertPool_ValidCACert (and the mixed-PEM test) to assert the actual CA
cert was added to the pool: parse the generated PEM from generateTestCACert (or
re-read the file written by writeTestFile) into an x509.Certificate, then verify
that the pool returned by loadCACertPool contains that certificate (e.g., by
checking pool.Subjects() includes the cert.RawSubject or otherwise matching the
cert's Raw bytes). Apply the same presence assertion in the mixed-PEM test where
the private key block should be ignored and only the CA cert should be present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 02d8cbab-e2ee-4748-ba24-e2372f220670

📥 Commits

Reviewing files that changed from the base of the PR and between bd96550 and 2769128.

📒 Files selected for processing (9)
  • charts/stackrox-mcp/templates/_helpers.tpl
  • charts/stackrox-mcp/templates/central-ca-secret.yaml
  • charts/stackrox-mcp/templates/configmap.yaml
  • charts/stackrox-mcp/templates/deployment.yaml
  • charts/stackrox-mcp/values.yaml
  • internal/client/client.go
  • internal/client/client_test.go
  • internal/config/config.go
  • internal/config/config_test.go

Comment thread internal/client/client.go Outdated
@mtodor mtodor force-pushed the mtodor/ROX-33250-add-self-signed-certs branch from 2769128 to 814901f Compare April 30, 2026 12:18
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@charts/stackrox-mcp/templates/_helpers.tpl`:
- Around line 87-93: The template stackrox-mcp.centralCASecretName can produce
names >63 chars when appending "-central-ca" to include "stackrox-mcp.fullname";
truncate the fullname portion before adding the suffix so the total DNS label
length stays <=63. Update the else branch to take include
"stackrox-mcp.fullname" . and pipe it through a truncation (e.g., sprig's trunc)
to 52 characters (63 - len("-central-ca") = 52), then append "-central-ca" so
the resulting secret name always fits Kubernetes DNS label limits.

In `@internal/client/client_test.go`:
- Around line 545-558: The loop using conn.GetState and conn.WaitForStateChange
can exit due to timeout and still satisfy assert.NotEqual, hiding regressions;
modify the loop in the test to record whether connectivity.TransientFailure was
actually observed (e.g., set a boolean observedTransientFailure when state ==
connectivity.TransientFailure) and after the loop assert that
observedTransientFailure is true (or call t.Fatalf on timeout) instead of only
asserting conn.GetState() != connectivity.Ready; reference conn.GetState,
conn.WaitForStateChange, connectivity.TransientFailure, and connectivity.Ready
when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 42f20684-e724-47bc-9ba9-f8bd8f33a92a

📥 Commits

Reviewing files that changed from the base of the PR and between 2769128 and 814901f.

📒 Files selected for processing (9)
  • charts/stackrox-mcp/templates/_helpers.tpl
  • charts/stackrox-mcp/templates/central-ca-secret.yaml
  • charts/stackrox-mcp/templates/configmap.yaml
  • charts/stackrox-mcp/templates/deployment.yaml
  • charts/stackrox-mcp/values.yaml
  • internal/client/client.go
  • internal/client/client_test.go
  • internal/config/config.go
  • internal/config/config_test.go

Comment on lines +87 to +93
{{- define "stackrox-mcp.centralCASecretName" -}}
{{- if .Values.centralCACert.existingSecretName }}
{{- .Values.centralCACert.existingSecretName }}
{{- else }}
{{- include "stackrox-mcp.fullname" . }}-central-ca
{{- end }}
{{- end }}
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

Truncate CA secret name after suffixing to avoid invalid Kubernetes names.

If stackrox-mcp.fullname is already near 63 chars, adding -central-ca can exceed the DNS label limit and break installs for long release names.

🔧 Proposed fix
 {{- define "stackrox-mcp.centralCASecretName" -}}
 {{- if .Values.centralCACert.existingSecretName }}
 {{- .Values.centralCACert.existingSecretName }}
 {{- else }}
-{{- include "stackrox-mcp.fullname" . }}-central-ca
+{{- printf "%s-central-ca" (include "stackrox-mcp.fullname" .) | trunc 63 | trimSuffix "-" }}
 {{- end }}
 {{- end }}
-  name: {{ include "stackrox-mcp.fullname" . }}-central-ca
+  name: {{ include "stackrox-mcp.centralCASecretName" . }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- define "stackrox-mcp.centralCASecretName" -}}
{{- if .Values.centralCACert.existingSecretName }}
{{- .Values.centralCACert.existingSecretName }}
{{- else }}
{{- include "stackrox-mcp.fullname" . }}-central-ca
{{- end }}
{{- end }}
{{- define "stackrox-mcp.centralCASecretName" -}}
{{- if .Values.centralCACert.existingSecretName }}
{{- .Values.centralCACert.existingSecretName }}
{{- else }}
{{- printf "%s-central-ca" (include "stackrox-mcp.fullname" .) | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@charts/stackrox-mcp/templates/_helpers.tpl` around lines 87 - 93, The
template stackrox-mcp.centralCASecretName can produce names >63 chars when
appending "-central-ca" to include "stackrox-mcp.fullname"; truncate the
fullname portion before adding the suffix so the total DNS label length stays
<=63. Update the else branch to take include "stackrox-mcp.fullname" . and pipe
it through a truncation (e.g., sprig's trunc) to 52 characters (63 -
len("-central-ca") = 52), then append "-central-ca" so the resulting secret name
always fits Kubernetes DNS label limits.

Comment on lines +545 to +558
for {
state := conn.GetState()
if state == connectivity.TransientFailure {
break
}

if !conn.WaitForStateChange(ctx, state) {
break
}
}

assert.NotEqual(t, connectivity.Ready, conn.GetState(),
"connection must not reach Ready state without CA cert for self-signed server")
}
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

Negative TLS test can pass without proving handshake rejection

Line 551 breaks on timeout and still passes with NotEqual(Ready), which can hide regressions (e.g., connection never actually attempted/failed). The test should assert it observed TransientFailure (or explicitly fail on timeout) to validate the rejection path.

Suggested fix
-	for {
+	reachedTransientFailure := false
+	for {
 		state := conn.GetState()
 		if state == connectivity.TransientFailure {
+			reachedTransientFailure = true
 			break
 		}
 
 		if !conn.WaitForStateChange(ctx, state) {
-			break
+			t.Fatalf("timeout waiting for TransientFailure; last state: %s", state)
 		}
 	}
 
-	assert.NotEqual(t, connectivity.Ready, conn.GetState(),
-		"connection must not reach Ready state without CA cert for self-signed server")
+	assert.True(t, reachedTransientFailure,
+		"expected TransientFailure without CA cert for self-signed server")
+	assert.NotEqual(t, connectivity.Ready, conn.GetState(),
+		"connection must not reach Ready state without CA cert for self-signed server")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/client/client_test.go` around lines 545 - 558, The loop using
conn.GetState and conn.WaitForStateChange can exit due to timeout and still
satisfy assert.NotEqual, hiding regressions; modify the loop in the test to
record whether connectivity.TransientFailure was actually observed (e.g., set a
boolean observedTransientFailure when state == connectivity.TransientFailure)
and after the loop assert that observedTransientFailure is true (or call
t.Fatalf on timeout) instead of only asserting conn.GetState() !=
connectivity.Ready; reference conn.GetState, conn.WaitForStateChange,
connectivity.TransientFailure, and connectivity.Ready when making the change.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants