Skip to content

Release v0.13.0 (minor) - #586

Open
github-actions[bot] wants to merge 50 commits into
mainfrom
develop
Open

github-actions[bot] wants to merge 50 commits into
mainfrom
develop

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Features

  • feat(metrics): add authenticated HTTPS serving behind an opt-in (e5703ae)
  • feat: per-policy SAN and authorization-extension allowlists for autosign (1bfa0c0)

Fixes

Refactoring

  • refactor: apply the expanded golangci-lint findings (779153f)
  • refactor: rename SigningPolicy 'pattern' field to 'certnames' (866335b)

Documentation

  • docs: make updating the documentation part of the change (d59fedf)
  • docs: stop advertising JVM auto-tuning that does not happen (baa6b55)
  • docs: fix the remaining broken example and two claims about behaviour (93d4757)
  • docs: add a guide for connecting agents (a61c135)
  • docs: close the gaps between the reference pages and the CRDs (ab6c28e)
  • docs: explain that no SigningPolicy means deny-all (aab8ce7)
  • docs: correct behaviour the controller does not have (7397a42)
  • docs: correct manifest examples that the API server rejects (3e1eeb5)
  • docs: align the SigningPolicy documentation with the renamed field and the guards (49ecfa3)

Build and CI

  • ci: expand golangci-lint to the common operator linter set (ae42cba)
  • ci: add golangci-lint configuration (c548647)

Chores

Files changed
 .github/workflows/_conforma-validate.yaml          |   2 +-
 .golangci.yml                                      |  80 ++++++
 AGENT.md                                           |  51 +++-
 README.md                                          |   2 +-
 api/v1alpha1/bool_default_test.go                  |  15 +-
 .../certificateauthority_validation_test.go        |  17 +-
 api/v1alpha1/javaargs_default_test.go              |  36 +++
 api/v1alpha1/server_types.go                       |   6 +-
 api/v1alpha1/signingpolicy_types.go                |  35 ++-
 api/v1alpha1/zz_generated.deepcopy.go              |  24 +-
 charts/openvox-operator/README.md                  |   5 +
 .../crds/openvox.voxpupuli.org_servers.yaml        |   9 +-
 .../openvox.voxpupuli.org_signingpolicies.yaml     |  76 +++++-
 .../templates/certmanager-issuer.yaml              |  47 ++++
 charts/openvox-operator/templates/deployment.yaml  |  23 +-
 .../templates/metrics-auth-rbac.yaml               |  46 ++++
 .../templates/metrics-certmanager.yaml             |  28 +++
 .../openvox-operator/templates/servicemonitor.yaml |  19 ++
 .../templates/webhook-certmanager.yaml             |  42 ----
 .../tests/certmanager-issuer_test.yaml             |  74 ++++++
 .../tests/webhook-certmanager_test.yaml            |  54 +----
 charts/openvox-operator/values.schema.json         |  30 +++
 charts/openvox-operator/values.yaml                |  17 ++
 charts/openvox-stack/templates/signingpolicy.yaml  |  32 ++-
 charts/openvox-stack/tests/signingpolicy_test.yaml |  47 +++-
 charts/openvox-stack/values.yaml                   |   7 +
 cmd/autosign/policy.go                             | 192 ++++++++++++++-
 cmd/autosign/policy_test.go                        | 270 +++++++++++++++++++--
 cmd/enc/classifier.go                              |  37 +--
 cmd/enc/classifier_test.go                         |  11 +-
 cmd/main.go                                        |  49 +++-
 cmd/main_test.go                                   |  54 +++++
 cmd/mock/main.go                                   |  18 +-
 cmd/mock/main_test.go                              |  20 +-
 cmd/report/processor.go                            |   5 +-
 .../crd/bases/openvox.voxpupuli.org_servers.yaml   |   9 +-
 .../openvox.voxpupuli.org_signingpolicies.yaml     |  76 +++++-
 config/rbac/role.yaml                              |   9 +
 config/samples/signingpolicy.yaml                  |   4 +-
 docs/_snippets/features.md                         |   4 +-
 docs/concepts/architecture.md                      |   4 +-
 docs/concepts/certificate-signing.md               |   7 +-
 docs/concepts/code-deployment.md                   |  24 +-
 docs/concepts/config-rollout.md                    |  17 +-
 docs/concepts/database.md                          |  15 +-
 docs/concepts/external-node-classification.md      |   2 +-
 docs/concepts/gateway-api.md                       |   5 +-
 docs/concepts/report-processing.md                 |   2 +-
 docs/examples/index.md                             |  10 +-
 docs/getting-started/quickstart.md                 |  14 +-
 docs/guides/ca-import.md                           |   2 +-
 docs/guides/connecting-agents.md                   | 163 +++++++++++++
 docs/guides/monitoring.md                          |  72 ++++++
 docs/index.md                                      |   2 +-
 docs/reference/binaries.md                         |  13 +-
 docs/reference/certificate.md                      |  14 ++
 docs/reference/certificateauthority.md             |   3 +-
 docs/reference/config.md                           |  17 +-
 docs/reference/database.md                         |  13 +-
 docs/reference/index.md                            |  27 ++-
 docs/reference/nodeclassifier.md                   |   5 +-
 docs/reference/reportprocessor.md                  |   1 +
 docs/reference/server.md                           |  15 +-
 docs/reference/signingpolicy.md                    | 113 ++++++++-
 docs/troubleshooting.md                            |  67 ++++-
 go.mod                                             |  47 +++-
 go.sum                                             |  61 +++--
 images/openvox-agent/Containerfile                 |   2 +-
 images/openvox-db/Containerfile                    |   2 +-
 images/openvox-e2e-code/Containerfile              |   2 +-
 .../environments/production/Puppetfile             |   2 +-
 images/openvox-mock/Containerfile                  |   2 +-
 images/openvox-operator/Containerfile              |   2 +-
 images/openvox-server-reference/Containerfile      |   2 +-
 images/openvox-server/Containerfile                |   9 +-
 images/openvox-versions.yaml                       |  10 +-
 internal/controller/ca_config_uniqueness_test.go   |   2 +-
 internal/controller/certificate_controller.go      |  15 +-
 internal/controller/certificate_controller_test.go |  15 +-
 internal/controller/certificate_derived_test.go    |  14 +-
 internal/controller/certificate_signing.go         |  15 +-
 internal/controller/certificate_spec_drift_test.go |  18 +-
 .../controller/certificateauthority_controller.go  |  10 +-
 .../certificateauthority_controller_test.go        |  44 ++--
 internal/controller/certificateauthority_job.go    |   8 +-
 .../controller/certificateauthority_job_test.go    |  10 +-
 internal/controller/certificateauthority_pvc.go    |   4 +-
 internal/controller/certificateauthority_rbac.go   |  11 +-
 .../controller/certificateauthority_service.go     |   4 +-
 .../certificateauthority_setup_backoff_test.go     |  40 +--
 .../controller/certificateauthority_signing.go     |   6 +-
 internal/controller/config_autosign.go             |  66 +++--
 internal/controller/config_autosign_test.go        |  78 +++++-
 internal/controller/config_controller.go           |   6 +-
 internal/controller/config_controller_test.go      |  30 +--
 internal/controller/config_enc.go                  |   4 +-
 internal/controller/config_rendering.go            |  23 +-
 internal/controller/config_rendering_test.go       |  20 +-
 internal/controller/config_serviceaccount.go       |   4 +-
 internal/controller/database_controller.go         |  27 ++-
 internal/controller/database_deployment.go         |   8 +-
 internal/controller/helpers.go                     |  14 +-
 internal/controller/helpers_test.go                |  23 +-
 internal/controller/image_pull_settings_test.go    |   2 +-
 internal/controller/labels.go                      |   9 -
 internal/controller/observed_generation_test.go    |   2 +-
 internal/controller/ownership_test.go              |   4 +-
 internal/controller/pause_test.go                  |   2 +-
 internal/controller/pool_controller.go             |  15 +-
 internal/controller/pool_controller_test.go        |  14 +-
 internal/controller/pool_hostname_conflict_test.go |  36 +--
 internal/controller/reportprocessor_controller.go  |  17 +-
 .../reportprocessor_controller_status_test.go      |   2 +-
 .../controller/reportprocessor_controller_test.go  |   2 +-
 internal/controller/securitycontext.go             |  12 +-
 internal/controller/server_controller.go           |  37 +--
 internal/controller/server_controller_test.go      |  31 ++-
 internal/controller/server_deployment.go           |  32 ++-
 internal/controller/server_deployment_test.go      |   4 +-
 internal/controller/testutil_test.go               |  34 +--
 internal/puppet/oids.go                            |  32 +++
 .../webhook/certificateauthority_delete_test.go    |   3 +-
 .../webhook/certificateauthority_webhook_test.go   |   3 +-
 internal/webhook/helpers.go                        |   4 +-
 internal/webhook/signingpolicy_webhook.go          |  46 +++-
 internal/webhook/signingpolicy_webhook_test.go     |  50 +++-
 mkdocs.yml                                         |   1 +
 tests/e2e/autosign-policy/chainsaw-test.yaml       |  77 ++++++
 128 files changed, 2661 insertions(+), 679 deletions(-)

Release prediction

Predicted release: v0.12.0 -> v0.13.0 (minor)
2 feature(s) 15 fix(es)


Source: develop | Target: main | Trigger: Successful CI on develop

Auto-generated by GitHub Actions - updated automatically on new commits.
Replace this text, for example with a Copilot-written summary, and it will be left alone.

slauger and others added 6 commits August 6, 2026 11:28
The autosign policy only checked certname patterns, exact CSR-attribute
values, and DNS SANs. It ignored privileged authorization extensions and
non-DNS SANs, and "any: true" short-circuited before any of these checks.
A node whose CSR matched a policy (or any CSR when an any:true policy
existed) could embed pp_cli_auth=true and, since the CA auth.conf grants
admin to any cert carrying that extension, obtain a CA-admin certificate.
IP/URI SANs could likewise impersonate services because only DNS SANs
were validated.

Introduce a fail-closed guard plane on SigningPolicy, enforced for every
policy including any:true:

- extensions: privileged authorization extensions (the
  1.3.6.1.4.1.34380.1.3 arc: pp_cli_auth, pp_authorization, pp_auth_token)
  are denied unless explicitly allow-listed. The gate is OID-prefix based,
  so unknown authorization-arc OIDs cannot be allow-listed and are always
  denied. Trusted-fact extensions (...1.1.* arc) are not gated.
- ipAltNames / uriAltNames / emailAltNames: fail-closed allowlists for the
  remaining SAN types, mirroring dnsAltNames. IPs use CIDR containment;
  URIs/emails use a wildcard matcher whose '*' spans '/' and '@'.

The guard plane is evaluated before the any:true short-circuit so no
policy can implicitly waive escalation protection. The renderer emits the
guard fields for every policy (including any:true), and the webhook
validates CIDR entries and known extension names. CA-level allow-* flags
are unchanged; enforcement now lives in the autosign binary and policy.

Refs #506
The certname-matching field was the only one that did not name what it
matches (unlike dnsAltNames/ipAltNames/extensions/csrAttributes), so a
reader could not tell 'pattern.allow' matched the certname. Rename it to
'certnames' for a self-describing, consistent spec. Breaking change on the
v1alpha1 API, batched with the SigningPolicy rework in this branch.

Also render the new guard fields (ipAltNames/uriAltNames/emailAltNames/
extensions) in the openvox-stack Helm SigningPolicy template so they are
usable via the chart.

Refs #506
The guard plane closed the pp_cli_auth path but not the one the CA
auth.conf opens beside it. builtinAuthRules grants CA admin to two things:
the extension, and the operator-signing certname. The certname is derived
as {ca}-operator and is therefore predictable, and the guard never looks at
it - it is checked only in the match plane, which any: true skips.

The operator now renders that name into the policy as reservedCertnames,
and the binary refuses it before any policy is consulted, so no policy can
hand it out. The name is derived from one helper used by the auth.conf
rendering, the operator-signing certificate and the reservation alike; a
drift between them would reserve one name while granting admin to another.

Second, the subject is now bound to the requested certname. puppetserver
takes the certname from the request path and passes it as the argument,
while the CN lives in the CSR subject, and nothing compared the two. The
policy would then judge one name while the certificate is issued carrying
another. Whether puppetserver rejects that first is an upstream question
this no longer depends on.

Scope, honestly: with the operator's own certificate in place the name is
taken, so puppetserver would refuse a second CSR for it anyway. The
reservation covers the window before that certificate exists and the state
after it is removed. That is also why the e2e step asserts the rendered
policy rather than the CA's signed directory - the latter would pass
without the reservation and prove nothing.
…d the guards

The rename from pattern to certnames had reached the reference page but not
the prose elsewhere: the overview table, two places in the architecture
concept and the feature snippet still named a field that no longer exists,
and binaries.md described the old evaluation model.

Also documents what the reference did not cover yet: the two checks that
precede every policy. Reserved certnames, because the CA auth.conf grants
admin rights to the operator-signing certname, and subject binding, because
puppetserver passes the certname as an argument while the CN lives in the
CSR subject. Both apply to any: true, which is the part worth stating
explicitly - the field reads like an escape hatch and is not one.

The chart values gain an example of the fail-closed allowlists, since the
existing ones only showed any and csrAttributes. helm-docs produces no
change from it.

Checked: no occurrence of pattern as a field name remains outside the word
operator pattern, and the cross-reference anchor resolves.
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
slauger and others added 11 commits September 4, 2026 00:19
Three classes of error that make a copied example fail rather than mislead
slowly.

spec.code is a list of sources, not a single one. Ten examples showed it as
an object; the API server answers 'unknown field spec.code.image' and
refuses the manifest. Verified against the CRD with kubectl apply
--dry-run=server: the list form is accepted, the object form is not.

The image tags 8.12.1 and 8.13.0 appeared at ten places and cannot exist.
images/openvox-versions.yaml states that the image name encodes the OpenVox
major while the tag carries the operator release version; the published tags
are 0.12.0, develop and latest.

Three label selectors in the troubleshooting and CA import guides do not
exist in labels.go: app.kubernetes.io/instance is never set,
app.kubernetes.io/name is openvox rather than openvox-server, and the CA
label is certificateauthority without a hyphen. A selector that matches
nothing is worse than no command at all, because it reads like an answer.

Also corrects two resource names: the Config ConfigMap is {name}-config,
and the CA setup Job is {ca}-ca-setup. The remaining names in the reference
tables were checked against the code and are correct.
The Error phase is documented for Config, Server and Database as
'reconciliation failed'. It exists in the CRD enum but no code path assigns
it: a failing reconcile leaves the phase where it was and reports the reason
in a condition. Someone waiting for phase Error waits forever, which is the
worst kind of wrong - it looks like a working diagnosis. Replaced with the
condition to watch, and the jsonpath was checked against a live object.

The Gateway API concept showed serverRef on a Pool. That field exists
neither on the CRD nor in the chart; a Server names its pools through
poolRefs and the Pool selects those pods. Removed, and the direction of the
relationship spelled out, since getting it backwards is the natural guess.

Added what the code does but nothing described: renewal reuses the existing
private key deliberately, because the CA renews for the same public key. So
renewal extends validity without rotating the key, and replacing a key needs
a new Certificate.

Checked and left alone: the Renewing phase description is already accurate,
and no page claims renewal generates a new key. The review report asserted
both; neither held up.
The operator writes autosign = /usr/local/bin/openvox-autosign as soon as a
CertificateAuthority exists, regardless of whether any SigningPolicy does,
and evaluatePolicies returns false for an empty list. An empty policy list is
therefore deny-all rather than off, which is the state every fresh install
starts in.

Nothing surfaces it: the servers run, the Config reports Running, and every
agent sits in --waitforcert until it gives up. Documented in the quickstart
and in the agent section of the troubleshooting guide, with the manual
puppetserver ca sign path as the immediate way out.

Also separates two cases the troubleshooting guide conflated. A Certificate
resource stuck in Pending is almost never a policy problem: for an internal
CA the operator signs its own Certificates over the CA API with the operator
signing certificate, so autosign is not involved. That entry now lists the
causes that do apply - CA not ready, signing certificate not yet available,
certname already claimed, external CA - each with a command that reads the
condition rather than the phase. Policies govern agents, and that is where
the note now lives.
Compared every documented field against the generated CRDs, in both
directions.

observedGeneration exists on all nine status types since #549 but appeared on
two reference pages. Added to the remaining seven, with the sentence that
makes it useful: a value below metadata.generation means the rest of the
status has not caught up yet.

Certificate gained signedSpecHash and effectiveDNSAltNames in the status
table. Both drive behaviour a reader needs to predict - the first decides
when a certificate is re-signed, the second says which alt names it is
actually issued for once Pools contribute.

Server gained readOnlyRootFilesystem, the per-Server override added in #575.

The ImageSpec table still listed defaults that #549 and #576 removed:
repository ghcr.io/slauger/openvox-server-8, tag latest, pullPolicy
IfNotPresent. It contradicted the prose directly beneath it. Corrected, and
the reason spelled out - a nested default is applied whether or not the
parent object was given, so a defaulted field can never mean inherit - plus
the pullSecrets rule that a Server list replaces rather than extends.

Checked and found correct: every other documented default matches its CRD,
and no reference page names a field that does not exist.
The user documentation never showed an agent connecting. puppet agent,
--waitforcert, ca_server and puppetserver ca sign appeared nowhere, so the
moment the product exists for was the one step a reader had to work out alone.

The guide covers what an agent needs, where to point it, running one inside
the cluster and reaching one from outside, signing by hand when no policy
matches, revoking, and how to tell from the server side that it worked.

Two things it states that are easy to get wrong. ca_server is only needed
once the CA and the compilers are in separate pools - with the default
poolRefs [ca, server] one address serves both, which is why the setting
appears nowhere in this repository. And puppet agent --test returns 2 when it
applied changes, so a Job that treats non-zero as failure reports a
successful run as broken.

It also names what the agent image is: openvox-agent-<major> is built by CI
and not by the release workflow, so it carries only the develop tag - no
latest, no version. Documented as the test artifact it is, rather than
implying it is a supported way to run agents.

Every claim was checked: the Service default is ClusterIP, the Deployment
carries the Server name, crlRefreshInterval defaults to 5m, and the image
tags are what ghcr actually holds.
Extracted all 42 manifests from the documentation and ran them against a
real API server with kubectl apply --dry-run=server. One was rejected for a
reason a reader could not guess: the NodeClassifier page abbreviated the
image block as 'image: ...', and the resulting error talks about a type
mismatch rather than the ellipsis. Replaced with the real block; all 42 are
accepted now.

The Database concept claimed the Config controller waits when the Database
is not Running. It does not: renderPuppetDBConf returns a puppetdb.conf
without server_urls and the servers start regardless. Together with
soft_write_failure = true, which the operator sets in every path, a server in
that state compiles catalogs normally while reports and exported resources go
nowhere and nothing is raised. That resolves itself during bring-up and only
bites when the Database never becomes ready, so the symptom is an empty
PuppetDB rather than an error.

Monitoring now states that CertificateAuthority and Certificate write the
same expiry metric with the same labels and nothing distinguishing them, so
the .*-ca matcher in the CA alert is a naming convention and not a guarantee.
Added an absence rule for the CRL metric: a staleness alert cannot fire on a
series that was never created, which is exactly the case when the operator
never got far enough to refresh one.
README and the feature list promise 'Heap size calculated from memory limits
(90%) - no manual -Xmx tuning needed'. The controller does contain that
calculation, but it is unreachable: ServerSpec.JavaArgs carries a CRD default,
so the field is never empty and the first branch of resolveJavaArgs always
wins. Every Server runs with -Xms512m -Xmx1024m no matter how much memory it
is given.

Replaced the claim with what actually applies, and noted it on the field in
the Server reference with a link to #592, where the fix is tracked. Once the
default is removed the derivation works and the wording can go back.

Third instance of the same pattern after #550 and #576: a CRD default takes
away the empty state a fallback depends on.
docs: correct and complete the documentation against the code
…n-allowlists

feat: per-policy SAN and authorization-extension allowlists for autosign
resolveJavaArgs sizes the JVM heap at 90 percent of the pod's memory limit
when javaArgs is unset. The CRD default made that unreachable: the field was
never empty, the explicit branch always won, and every Server ran with
-Xms512m -Xmx1024m no matter how much memory it was given. A Server with 8Gi
used 1Gi of it.

Third instance of one mistake after #550 and #576. A nested or plain default
removes the empty state a fallback depends on, so the default belongs in the
chart where it is visible, not in the CRD where it disables code.

The existing unit tests did not catch this and could not: they call
resolveJavaArgs directly and therefore pass whether or not the field can ever
be empty in a real cluster. Added the round-trip through the API server
instead, which fails against the old CRD with the materialised value in the
message.

Restores the auto-tuning statement in the README and the feature list, which
I had corrected to match the broken behaviour rather than the intended one.

Closes #592
Every SigningPolicy edit rolled the CA pod. The policy Secret was mounted
with SubPath, which the kubelet never refreshes, so the operator compensated
with a hash annotation on the pod template. Because the CA Deployment uses
the Recreate strategy, that meant a short outage - no signing, no CRL - for a
change that alters no running state. On a VM-based Puppet, editing autosign
configuration has no serving impact at all.

Nothing downstream required the restart: openvox-autosign is executed per CSR
and reads the file each time. Only the mount did.

Mount the Secret as a directory instead, the way the CRL Secret already is
for the same reason, and pass the file to the binary with --config. The hash
annotation is gone, so a policy edit no longer touches the pod template.

The trade-off is timing: a change now takes effect within the kubelet sync
period, up to about a minute, rather than immediately after a restart. For a
rule deciding which CSRs get signed, that is the better end of the trade.

The former test asserted the annotation exists; it now asserts the opposite
and that the mount carries no SubPath, since a SubPath would silently
reintroduce the whole problem. The rendered puppet.conf is checked for the
config path too - a wrong one denies every CSR and would otherwise only
surface in an end-to-end run.

Closes #588
@github-actions github-actions Bot changed the title Release develop -> main (no release) Release v0.13.0 (minor) Sep 3, 2026
renovate Bot and others added 11 commits September 4, 2026 02:58
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
The metrics endpoint served plaintext HTTP to anything that could reach the
pod. It carries no key material, but it lists every certificate the operator
manages and when each expires, which is a ready-made inventory for picking a
moment.

metrics.secure turns on secure serving plus the controller-runtime filter,
which verifies each scrape against the API server with a TokenReview and a
SubjectAccessReview. The chart adds a metrics-reader ClusterRole granting get
on /metrics; nothing is bound to it by default, so a scraper needs an explicit
grant.

The default stays off. Turning it on switches the endpoint to HTTPS and makes
a token mandatory, and Prometheus reports the target as down without saying
why. That belongs in a release note rather than in a patch.

Two details worth stating.

The auth-delegation rules live in their own ClusterRole. TokenReview and
SubjectAccessReview are cluster-scoped, so the namespace-scoped Role the chart
otherwise renders cannot grant them - the filter would reject every scrape in
namespace mode.

The issuer chain moved out of the webhook template into its own file. It is
now rendered when either the webhook or the metrics endpoint asks for a
certificate, so secure metrics no longer require webhooks to be enabled.

For the certificate itself: cert-manager by default, a supplied Secret via
metrics.tls.certSecret, or a self-signed one generated at startup. The
protection comes from the filter rather than the certificate, which is why the
self-signed path is usable at all - the bundled ServiceMonitor verifies
against the CA when cert-manager issues it and skips verification otherwise.

Refs #510
The documentation had never been checked against the code until the review on
2026-09-03. It found manifests the API server rejects, three label selectors
that match nothing, a phase no controller sets, image tags that cannot exist
and a defaults table contradicting the prose beneath it. Every one of them
looked plausible on the page, which is why none had been noticed.

AGENT.md described docs/ as design docs and architecture diagrams. It is 35
pages of user-facing reference covering every field, phase, condition and
metric - closer to the API than to design notes, and it drifts the same way an
API would.

Adds the rule that a change altering observable behaviour updates the
documentation in the same commit, a table of what to touch for each kind of
change, and the checks that would have caught the drift: manifests through
kubectl apply --dry-run=server, selectors against labels.go, documented fields
against the generated CRDs. The commands are runnable rather than sketched.

Also asks for the honest version when a claim cannot be verified - saying so
in the text beats leaving it to look confirmed.
feat(metrics): authenticated HTTPS serving behind an opt-in (#510)
docs: make updating the documentation part of the change
The build stage patched openvoxserver-ca with sed to survive rootless
containers: it commented out the symlink_to_old_cadir call in setup.rb
and every FileUtils.chown in file_system.rb, because both raised
Errno::EPERM without CAP_CHOWN.

Upstream fixed this properly in openvoxserver-ca 3.2.0: PR #33 routes
all ownership changes through an ensure_ownership helper that is a
no-op when not running as root, and forcibly_symlink uses the same
helper, so the cadir compatibility symlink now works rootless as well
(it lands in the ssl emptyDir and is harmless). The openvox-server
8.15.2 tarball ships openvoxserver-ca 3.2.1, which includes the fix.

The patch had also been degrading silently: since 3.2.0 the chown sed
was editing the inside of ensure_ownership, and both find|sed commands
suppressed all errors, so a non-matching pattern would never have
failed the build.

OpenVoxProject/openvoxserver-ca#33
fix(image): drop obsolete openvoxserver-ca rootless patches
golangci-lint ran with the bare v2 standard preset so far. The new
config keeps the standard linters and adds errorlint, gosec, misspell,
revive and unconvert, plus gofmt and goimports as enforced formatters.

Generated files are excluded via the lax mode, gosec is disabled for
test files and the revive exported-comment rule is off to avoid noise
from kubebuilder scaffolding.
Fix the issues the new golangci-lint config surfaced:

- read config files via filepath.Clean to satisfy gosec G304
- tighten ENC cache permissions to 0750/0600 (G301/G306)
- use errors.As instead of a type assertion in isNotFound (errorlint)
- give the mock server a ReadHeaderTimeout (G114) and annotate its
  debug log lines, which print CLI flag values and request data by
  design (G706)
- clamp intstrInt before the int32 conversion (G115)
- drop unnecessary conversions around gwapiv1.PortNumber (unconvert)
The golangci-lint defaults cap repeated findings (max-same-issues: 3),
so the first CI run hid additional G304 sites that only surfaced after
the first batch was fixed. Disable both caps so runs are exhaustive,
and wrap the remaining variable file reads in filepath.Clean.
slauger and others added 22 commits September 5, 2026 23:35
Aligns with what cluster-api, cert-manager, CloudNativePG,
external-secrets and the kubebuilder scaffolding enable: bodyclose,
copyloopvar, dogsled, durationcheck, gocritic, intrange, loggercheck,
modernize, nakedret, nilerr, nolintlint, prealloc and unparam, plus
asciicheck/bidichk to back the unicode-lint CI check on the Go level.

importas enforces the usual k8s import aliases (no-unaliased), which
in particular settles the split between plain and apierrors imports of
k8s.io/apimachinery/pkg/api/errors.
Mostly mechanical, largely via golangci-lint run --fix:

- unify k8s.io/apimachinery/pkg/api/errors imports on apierrors
  (importas); the unaliased form shadowed the stdlib errors name
- modernize rewrites: new(expr) instead of local ptr helpers (the
  int64Ptr/boolPtr/int32Ptr/fsGroupChangePolicyPtr helpers and their
  tests are gone), strings.SplitSeq, and friends
- rewrite if-else chains as switches, close fire-and-forget response
  bodies in the mock tests, preallocate slices where the capacity is
  known (gocritic, bodyclose, prealloc)
- drop test-helper parameters that only ever received one value and
  make the two no-panic tests assert through t (unparam)
- keep buildPodSecurityContext's uid parameter with an explained
  nolint: all workloads currently run as uid 1001, but the parameter
  stays symmetric with group/fsGroup
ci: expand golangci-lint configuration to the common operator linter set
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
chore(deps): update dependency puppetlabs-stdlib to v10.1.0
fix(deps): update module golang.org/x/vuln to v1.8.0
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ubi9/ubi 9.8-1789348643 -> 9.8-1789552280
ubi9/ubi-minimal 9.8-1789349365 -> 9.8-1789546276

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
GO-2026-6348 (heap memory exhaustion via HTTP/2 DATA frame fragmentation)
is reachable from our code through the controller-runtime event recorder,
so govulncheck reports it as called and the go/vulncheck job fails. The
advisory landed after develop last ran CI, which is why develop still shows
green while every open PR fails the job.

grpc is an indirect dependency, so Renovate does not bump it on its own.
Pinning it to the fixed release clears the finding; go mod tidy pulls
cel.dev/expr v0.25.2 along with it.

govulncheck now reports no called vulnerabilities.
chore(deps): combined dependency updates and grpc vulnerability fix
…9.8-1789646010 (#612)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
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.

1 participant