- adds ctrlEventQueue, a per-listener queue drained one event at a time on an on-demand goroutine, so listeners see events in emission order and a slow listener delays only itself
- replaces the goroutine-per-listener fan-out in notifyOfChange with enqueueing on each listener's queue
- holds the per-channel connectivityState lock from deciding a connectivity edge through queuing its event, so a disconnect is always queued before the reconnect that follows it
- collects ctrl events in tests under a lock instead of draining a channel inside Eventually conditions, removing a data race in the tests
- tests that the queue preserves order past a slow listener and resumes after draining
* fixesopenziti/ziti#4321 compare process hashes and fingerprints case-insensitively
- normalizes configured hashes and signer fingerprints to lowercase unseparated hex in the
PROCESS and PROCESS_MULTI stores, on load as well as on save, so values stored before this
change read back normalized without a migration
- replaces the PROCESS store's lowercase-only pass, PROCESS_MULTI stored its values verbatim
- adds store tests for both types covering configured values and values written straight to bolt
- updates the process multi patch round-trip test, the management API now returns normalized
values rather than the submitted strings
- removes the trailing newline from the signer fingerprint literal in the process and
process-multi OIDC posture tests
- records an api session's disconnect time when its last connection to the router goes away, and evicts its posture data once that time is older than the retention window
- clears the recorded disconnect when a session connects again or when posture data arrives for it, so a reconnect keeps its posture state
- reconciles recorded state against the connection tracker on each sweep, correcting a missed connect or disconnect notification without itself evicting, so a missed notification delays an eviction rather than losing or preventing one
- decides a reconnect and a removal under the same lock, and confirms the judged disconnect time when removing, so a verdict overtaken by a reconnect cannot delete a live session's posture data
- removes the posture instance history, which was retained per api session and never read
- adds tests for the eviction lifecycle, drift reconciliation, and contention between a reconnect and a sweep
Registry.Meter is reference counted: every call takes a reference, including calls
that find the meter already present, and only Dispose releases one. The enforcers
resolved a meter inside Run, so each run took a reference it never shed. Disposing
such a meter decrements the count instead of stopping it, so it leaves the registry
and keeps sampling.
- resolves each enforcer's meter once, where the enforcer is built, and holds it
- holds the timers alongside, though Timer is not reference counted, so a reader
is not left working out why one of two adjacent lookups was cached
- supplies the metrics in the session enforcer test, which builds the enforcer
directly to get a negative session timeout that NewSessionEnforcer refuses
Per enforcement cycle rather than per event, so the growth was one reference per
run. Unbounded over a controller's lifetime, but slow.
grep -rnE '\.(Meter|Histogram)\([^)]*\)\.(Mark|Update)' finds this shape; there are
no others left outside tests.
- treats a missing MAC posture response as a failed check instead of dereferencing nil state
- treats a missing domain posture response the same way
- fails a posture check whose subtype the router does not recognize, rather than calling the nil checker CtrlCheckToLogic returns for it
- adds UnsupportedCheckTypeError alongside the existing posture sentinels
- moves MacCheck.Evaluate to a pointer receiver, matching every other checker
- adds unit tests for the nil-data and unsupported-subtype paths
- adds Test_ServerConnCloseWritePropagationXgressTerminator, binding the host with SDK flow
control and forcing the dial onto ConnectV1 so the router bridges the two modes
- asserts the client's read ends at io.EOF while its own write side stays open, the case a
default dial does not reach because both ends then run xgress
- treats an os type declared with no versions as a match, as the controller does
- parses the reported os version only when a version constraint exists
- skips an unparsable declared version range with a warning instead of failing the check
- adds os check tests covering no-versions, unparsable input, and duplicate os type declarations
- normalizes reported MAC addresses in Instance.Apply to lowercase unseparated hex,
the form MAC posture check values are persisted in
- stores a new PostureResponse_Macs rather than mutating the incoming protobuf message
- compares the normalized addresses when deciding whether posture data changed, so a
re-reported separated address no longer reads as an update
- moves the normalizer to a new common/posture package, shared by controller/db and
router/posture, and hoists its regexp out of the per-call path
- guards the process-list posture response branch so TOTP-only responses no longer wipe cached process data and drop the token
- merges process-list responses by path, since SDKs report processes as per-path deltas
- replaces OS posture state on update instead of mutating a pointer shared with snapshots
- returns early when posture response unmarshalling fails instead of dispatching a partial message
- adds regression tests for posture response application
- adds chaos.EnsureUp, which waits for a component spec to be running, restarts the whole spec if it is not, then waits again so the recovery is asserted rather than assumed
- replaces the copied validate-then-restart block in nine fablab models, which returned the restart's own result and so reported success whether or not any router came back, since starting a component only launches it
- keeps private-ctrl-test's lifecycle router checks running after a recovery, which the early return had skipped entirely
- collapses links-test onto its start concurrency of 100, which it already used for both of its router starts
- adds an oidc-auth-test fablab model that exercises OIDC authentication end to end, with an event-forwarder component, an oidc-test-client, a ziti-prox-c component, and OIDC event/gossip/traffic validations
- moves zitirest out of the public API into separate test-only shims: an integration-test shim (tests/restclient.go) and a fablab shim (zititest/zitirest), and repoints upgrade-test at the fablab shim
- adds region-isolation network partitioning and packet capture on all interfaces to the chaos toolkit
- queues oidc-test-client results reporting on a single sender goroutine with a bounded write deadline, so a stalled results circuit can't block the traffic loops
- counts results events dropped when that queue fills and reports them to the collector as errors, so a reporting outage fails validation instead of looking like a clean run
- bounds the event forwarder's event and keepalive writes, and checks for shutdown on every send attempt, so an unreadable destination can't park the forward loop holding its lock
- logs read errors on collector connections instead of ending collection for that client silently
- bounds the oidc-auth-test debug server's header and body reads
- matches ziti-prox-c process matching to the version-agnostic binary name, so a version change still finds the running process
- points upgrade-test's per-service terminator count at the shared validations helper
- adds a fablab design summary doc
Test_ServerCloseListenerPropagation never received from errC, so it was the only test
in the file that did not wait for its hosting goroutine. Reading the last reply only
proves the data reached the wire: the SDK writes with SendAndWaitForWire, so the write
is still waiting to be told its buffer went out. The test body returning ran the
deferred conn, listener, context and context-teardown closes under it, which resolved
that wait as "channel closed" and failed a write whose data the client already had.
- waits on errC before returning, matching the other six tests in the file
The window is between the data hitting the wire and the sender being notified, so it
does not reproduce on an idle machine; it showed up on a loaded CI runner as a single
failure with no client-side error, which is what identified the ordering.
- withholds an enrollment's token and jwt from a non-admin caller when the owning
identity is an admin, in the identity detail mapper and the enrollment mapper
- scopes the identity enrollments subresource so a non-admin does not see
enrollments of admin identities, matching the top level /enrollments list
- threads the request context into MapIdentityToRestModel and
MapEnrollmentToRestModel so the mappers can see the caller's permissions
- adds an integration test over the identity detail, identity list, and identity
enrollments routes for every enrollment method
- adds the advisory to the 2.1.0 changelog
- removes a stray assignment that overwrote isCertExtendable with a literal true on every OIDC certificate authentication
- reports isCertExtendable as false over OIDC for cert authenticators not issued by the network, matching legacy cert auth
- adds coverage asserting the z_ice claim and current-api-session both report a 3rd party CA certificate as not extendable
* fixesopenziti/ziti#4094 accept first-party certs issued by a separate edge signing CA
- adds FirstPartyX509CertValidation and ThirdPartyX509CertValidation usages and an
intermediates field to the router data model public keys, deprecating
ClientX509CertValidation
- publishes config CA bundle roots as first-party anchors with their intermediates and
Ca store entries as third-party anchors; controller certs carry JWT validation only,
since a controller identity is never a CA and anchors no client cert chains
- builds router first-party and client cert trust pools from the published usages,
falling back to the deprecated usage against older controllers
- propagates the full signing cert chain between controllers via a new mesh
SigningCertChainHeader and persists whole chains in controller records
- removes the orphaned InstantStrategy.AddPublicKey, dead since public key sync moved
to controller list data
- gives each command dispatcher its own decoder registry so multiple in-process
controllers no longer decode into the last-started controller's managers
- adds a three-controller in-process HA test harness with a split signing PKI,
cluster formation and first-party cert integration tests
- trusts the edge signing CA when verifying router control channel certs
- adds a variadic additionalRoots parameter to VerifyLeafCertChain, applied to a
clone of the caller's pool so an identity's live tls.Configs are unaffected
- passes the edge enrollment signing CA bundle as additional roots when admitting
a router control channel connection, so a deployment whose signing CA sits
outside the controller's own trust bundle no longer has every router refused
- leaves the fingerprint check bound to the verified leaf, so the wider anchor set
changes which chains verify, not which routers are admitted
- covers the split-root case and the caller-pool guarantee in common/cert tests
- adds a package-level buildFlags string the linker sets at build time, parsed
into a capped list of [A-Z0-9_] names with blanks, duplicates, and malformed
tokens dropped
- returns those names in the new buildFlags field on /version, separate from
capabilities, and prints them under ziti version -v
- bumps edge-api to v0.36.0 for the buildFlags field
- reads the cost header via GetUint16Header, which returns (0, false) on a
wrong-width value, instead of decoding the raw header bytes and indexing past
the end of a short value
- matches the accessor already used for the same header in processUpdateBind
Bumps the all group with 1 update in the /dist/docker-images/ziti-controller directory: openziti/ziti-console-assets.
Updates `openziti/ziti-console-assets` from 4.1.2 to 4.5.1
---
updated-dependencies:
- dependency-name: openziti/ziti-console-assets
dependency-version: 4.2.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: all
...
Signed-off-by: dependabot[bot] <support@github.com>
- cancels the server on every doClient exit path, not just on success, so a
failed dial tears the server down promptly instead of leaving it blocked in
listener.Accept()
- propagates the client error out of doBoth/doBothExtJwt instead of returning
nil, so a failed traffic test exits non-zero and callers can retry
- adds a fablab model (zititest/models/upgrade-test) that stands up a multi-region HA controller cluster, routers, and loop4 sim traffic clients (SDK, ERT, ZET, tunneler) and drives repeatable in-place upgrade/downgrade iterations, re-bootstrapping in place each iteration
- exercises HA cluster disruption and recovery (snapshot restore, node rejoin) between iterations and validates steady-state traffic and expected terminators after each disruption
- honors the raft restartSelf setting on migration-snapshot restore, so a restored controller restarts itself instead of exiting, and promotes RestartController to a package-level function
- re-sends loop4 run-scenario requests to sims that reconnect mid-scenario, tracking each scenario's expected client set explicitly and ignoring results from clients outside it, so post-disruption client reconnection no longer stalls validation
- closes leaked loop4 sim-control connections on error and on supersession
- rotates component logs and pins the log-pipe binary to the local build, and drops router debug logging, to keep long iterating runs disk-bounded
- excludes the doc/unsettled/ scratch area for in-progress design docs from version control
- removes CryptoMethodHeader from the router's peer header request mappings, so a
dialing client's value no longer reaches circuit data
- removes the matching forwarding from all three xgress_edge dial paths
- drops the changelog entry for e2ee scheme negotiation, which this un-ships
- rebases onto current main (picks up the ext-jwt overlapping-kids test fix)
- pins github.com/openziti/sdk-golang/v2 to v2.0.0-pre4 (flaky-test fix) in both the root and
zititest modules
- bumps the remaining external and openziti dependencies to the latest within their current
major versions (channel v5.0.27, transport v2.0.220, otel, golang.org/x/exp, and others)
- regenerates the Component Updates and Bug Fixes section against v2.0.0
- serves the overlapping-kid JWKS providers over loopback HTTP instead of HTTPS: the hardened
JWKS resolver (GHSA-whjr) builds its own http.Transport and validates TLS against the system
roots, so a CA injected into http.DefaultTransport is no longer honored and the HTTPS JWKS
never resolved, leaving primary auth unable to succeed
- removes the now-dead default-transport TLS trust setup and the unused newTlsJwksServer helper
- transport is orthogonal to the kid/issuer disambiguation the test exercises
- nil-check state.Woken/state.Unlocked in MfaCheck.Evaluate before dereferencing
- align router wake/unlock semantics with the controller's PassedOnWake/PassedOnUnlock:
an absent or pre-MFA wake/unlock event passes; only a post-MFA event beyond the
grace period fails
- recover panics in EvaluatePostureCheck, converting them into a failed check
(denied access) instead of an unrecovered goroutine panic crashing the router
- add regression tests for router/posture MFA evaluation
- requires an API session token on CreateCircuitV3 requests and validates it,
covering signature, audience, token type, and revocation by token id,
identity, and api session
- takes the dialing identity from the validated token claims rather than the
router-supplied identity id, and rejects a request whose asserted identity
does not match the token subject
- adds the api session id to the log context, matching the V1 and V2 paths
- adds tests for a missing token, an invalid token, and a token belonging to a
different identity than the one asserted
- notes the advisory in the 2.1.0 release notes
- enforces issuer and audience claims inside TokenIssuerExtJwt.VerifyToken, which
previously verified only the token signature and resolved the signing key by kid
- closes the ziti-token-issuer-id header enrollment path accepting a validly-signed
token minted for a different audience or issuer that shares signing keys
- mirrors the runtime ext-jwt authentication and by-inspection enrollment paths so
all paths validate claims consistently
- adds a controller/model unit test covering foreign-audience, foreign-issuer, and
missing-audience rejection
- adds HardenedJwksResolver, a jwks.Resolver that fetches an external jwt signer's
jwksEndpoint with an http/https-only scheme check, a total timeout and a redirect cap
- adds JwksFetchPolicy, gating a fetch on both the URL hostname and the address being
connected to, applied to the first request and to every redirect hop
- hostname gate: deniedHostnames blocks, allowedHostnames is exclusive when set; entries
are an exact hostname or a '*.suffix' wildcard that matches subdomains at any depth but
never the suffix itself, normalized to lower case punycode without a trailing dot
- address gate: built-in blocked (metadata, link-local, link-local multicast,
unspecified), then deniedIPs, then allowedIPs, then blockPrivateAddresses,
first-match-wins with deny over allow
- keeps the gates independent, so neither can authorize what the other refuses; the
address check runs in the dialer against the resolved address, so a hostname that
resolves to a blocked address is refused
- adds the [edge.externalJwtSigners.jwksFetch] config section with compatible defaults:
empty hostname lists, blockPrivateAddresses false, timeout 5s, maxRedirects 5
- takes IP lists as a flat address or a CIDR block, hostname lists as names only, and
rejects an entry belonging to the other list at startup
- rejects a jwksEndpoint the policy refuses when an external jwt signer is created or
updated, and logs an existing signer whose endpoint the configuration now refuses
when the token issuer cache loads
- documents both gates, their deny-wins precedence, the accepted entry forms and the
wildcard matching rules in etc/ctrl.with.edge.yml and CHANGELOG.md