Security characteristics¶
A reference map of MKurator's security-relevant characteristics across design & architecture, runtime, and CI/CD & supply chain. Unlike the point-in-time Security review (a dated maintainer self-review) and the Assurance case (the claim/argument/evidence structure), this page is the living catalogue of controls with pointers to the code, ADRs, and workflows that implement them, so it can be kept current as the system evolves.
| Field | Value |
|---|---|
| Last updated | 2026-07-31 |
| Applies to | main (post v0.15.0) |
| Scope | The operator (controller-manager), its admission webhooks, the mqweb REST adapter, and the CI/CD supply chain. Excludes the customer's IBM MQ deployment and cluster-level controls (RBAC grants, network policy, secret backends) which are the deploying operator's responsibility. |
1. Trust boundaries¶
┌── cluster admin / CRD authors ──┐ ┌─────────── IBM MQ estate ───────────┐
│ QueueManagerConnection, Queue, │ │ mqweb REST endpoint (per QMC) │
│ Channel, Topic, AuthorityRecord│ │ presents a server TLS certificate │
└───────────────┬─────────────────┘ └──────────────────┬──────────────────┘
│ kube-apiserver (authn/z, admission) │ HTTPS (TLS)
┌──────────▼───────────┐ watch/patch ┌─────────────────▼─────────────────┐
│ MKurator controller │◄──────────────►│ mqweb REST adapter (internal/adapter│
│ (least-priv ClusterRole) │ /mqrest) — Basic / LTPA / mTLS │
└──────────┬───────────┘ └────────────────────────────────────┘
│ reads referenced Secrets (credentials, CA, client certs)
┌──────────▼───────────┐
│ Kubernetes Secrets │ (namespaced, RBAC-gated)
└──────────────────────┘
Untrusted / attacker-influenced inputs the design must tolerate: CRD spec fields (validated by admission webhooks), mqweb REST responses (parsed defensively; see fuzzing below), and referenced Secret contents. The controller holds no long-lived inbound listener other than the admission webhook and the metrics endpoint.
2. Design & architecture¶
2.1 Transport & TLS to IBM MQ¶
All queue-manager administration flows over the mqweb REST API over HTTPS (ADR-0002, ADR-0017). The adapter validates the mqweb server certificate against a configurable CA bundle (from the referenced Secret); hostname/SAN mismatches surface as TLS errors rather than being silently trusted. Transport is firewall-friendly HTTPS — no bespoke MQ ports.
2.2 Authentication to IBM MQ¶
Three modes, enum-validated on the CRD
(ADR-0027; internal/adapter/mqrest/auth.go):
| Mode | Material | Notes |
|---|---|---|
| Basic | user + password Secret | mqweb basic auth over TLS |
| LTPA | mqweb login → LTPA cookie | cookie cached; re-login on MQWB0104E/MQWB0112E, single-retry (no loops) |
| ClientCert | client key/cert Secret | mutual TLS to mqweb |
2.3 Admission webhooks¶
Validating admission webhooks (ADR-0009;
config/webhook/) reject malformed/unsafe CRs at admission time, with CEL and Go
validators. Webhook serving TLS is provisioned via cert-manager (webhook-server-cert);
the controller waits for the cert to be Ready before serving.
2.4 Least-privilege RBAC¶
The operator's ClusterRole (config/rbac/role.yaml) enumerates explicit
resources/verbs — no "*" wildcard resources or verbs. Leader election is a namespaced
Role; metrics auth is a separate scoped ClusterRole. The RBAC surface is audited in CI
(audit-rbac job → Polaris + kubeaudit).
2.5 Runtime hardening¶
The shipped pod runs hardened (charts/mkurator/values.yaml):
runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false,
seccompProfile: RuntimeDefault, and all Linux capabilities dropped. The manager binary
is built CGO-free / static.
2.6 Secret handling¶
- Credentials are read from namespaced Kubernetes Secrets, RBAC-gated; never inlined in CRDs.
- Structured logs pass through a redaction handler (
internal/logging/redact.go,redactHandler) that replaces sensitive values with[REDACTED]. - Secrets are never written to CR
.statusor Events — reconcile-error classification (internal/controller/events.go) emits categorised reasons, not credential material. - Per ADR-0023, the connection release path reads no Secrets, bounding where secret material is touched.
2.7 Connection / credential cache lifecycle¶
Cached mqweb transports are keyed and rotated on credential fingerprint change
(ADR-0023); rotation closes stale transports
rather than leaking them. Requeue jitter uses non-cryptographic math/rand by design
(scheduling only, no security dependence — Sonar S2245 accepted-safe; drift_resync.go).
3. CI/CD & supply chain¶
Every control below runs in GitHub Actions (.github/workflows/). All third-party actions
are pinned by commit SHA; workflow tokens follow least privilege (deny-all or read-only
baselines with per-job opt-in — OpenSSF Token-Permissions = 10).
| Class | Control | Where |
|---|---|---|
| SAST | CodeQL (Go) on every push/PR | codeql.yaml; golangci-lint incl. gosec |
| SCA (deps) | govulncheck (call-graph) and blocks PRs via the required test job; standalone Vulncheck workflow + weekly cron |
ci.yaml (task vuln:check), vulncheck.yaml |
| SCA policy | osv-scanner with a curated osv-scanner.toml (justified IgnoredVulns for unreachable/unfixable advisories) |
osv-scanner.toml |
| Dep updates | Dependabot and Renovate | .github/dependabot.yml, renovate.json |
| Secret scanning | gitleaks on every PR (full history) | ci.yaml (gitleaks job) |
| Static quality | SonarCloud analysis + Go coverage; accepted-safe/test-only findings suppressed as config-as-code | sonar-project.properties |
| Fuzzing | Native Go fuzzing of the untrusted mqweb DISPLAY parser (FuzzParseMQSCDisplayAttributes); a real crash corpus fails the job with no retry |
ci.yaml (fuzz job), internal/adapter/mqrest/mqsc_display_parse_fuzz_test.go |
| RBAC audit | Polaris + kubeaudit | ci.yaml (audit-rbac) |
| Signed releases | cosign keyless (OIDC) signatures, SLSA provenance attestation, and SBOM (SPDX) attestation on the container image; signed Helm chart artifacts | release.yaml |
| Merge gates | Protected main ruleset — required checks, 1 approval, dismiss-stale, rebase-only; admin PR bypass reserved for the maintainer |
ADR-0020 |
OpenSSF Scorecard posture¶
Maturity posture is tracked (ADR-0019); the Scorecard runs
weekly + on push (scorecard.yaml). Actionable checks are kept green (Token-Permissions,
Signed-Releases, SAST, Security-Policy, Pinned-Dependencies, Dependency-Update-Tool, Fuzzing).
Some low scores are structural, not defects, and are accepted:
- Maintained / Contributors — repository age (< 90 days) and a single maintaining org.
- Code-Review — an artifact of the solo maintainer's
--adminrebase-merge flow (the independent-review gate is enforced by process, see ADR-0020), not absence of review. - Branch-Protection — the Scorecard token cannot read rulesets (this repo uses rulesets, not classic branch protection), yielding a false internal error.
4. Residual risks & deployment-owned controls¶
- Network egress is deployment-owned: the controller egresses to each mqweb endpoint
(HTTPS) and the kube-apiserver. A cluster
NetworkPolicyrestricting egress is not shipped by the chart and should be applied by the deploying operator per their environment. - changelog-sync self-heal requires a GitHub App as a
protect-mainbypass actor; until activated it is inert (thesyncjob skips cleanly). The bypass actor is a privileged trust anchor and should be a dedicated, least-privilege App. - The Security review self-review is point-in-time; refresh it alongside this page when the security surface changes.
5. Verify it yourself¶
task vuln:check # govulncheck (call-graph) — 0 findings
osv-scanner --lockfile=go.mod # SCA vs curated osv-scanner.toml
task lint # golangci-lint incl. gosec
go test -run='^$' -fuzz='^FuzzParseMQSCDisplayAttributes$' -fuzztime=30s ./internal/adapter/mqrest/
task audit-rbac 2>/dev/null || bash hack/audit-rbac.sh # Polaris + kubeaudit
gh api repos/platformrelay/MKurator/actions/workflows --jq '.workflows[] | .name' # SAST/scans present
Maintenance: keep the “Last updated” date and the control table in sync with
.github/workflows/ and the referenced ADRs whenever the security surface changes.