9109 Commits

Author SHA1 Message Date
Paul Lorenz 110db6623b Merge pull request #4353 from openziti/fix/policy-enforcer-metric-refs
Hold the policy enforcers' meters rather than resolving one per run
2026-09-09 17:37:48 -04:00
Paul Lorenz 926a52c062 Merge pull request #4370 from openziti/ctrl-event-ordering
Deliver router ctrl events to each listener in order
2026-09-09 16:50:44 -04:00
Paul Lorenz 65de567eb1 Merge pull request #4356 from openziti/fix/posture-cache-eviction
Evict posture data for disconnected api sessions
2026-09-09 16:46:03 -04:00
Paul Lorenz 64e8910a87 Deliver router ctrl events to each listener in order. Fixes #4369
- 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
2026-09-08 15:25:02 -04:00
Andrew Martinez b4f6223ad1 fixes openziti/ziti#4321 compare process hashes and fingerprints case… (#4344)
* fixes openziti/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
2026-09-08 13:45:15 -04:00
Paul Lorenz 33eea80f33 Evict posture data for disconnected api sessions. Fixes #4323
- 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
2026-09-03 22:00:34 -04:00
Paul Lorenz b271a9e48d Merge pull request #4343 from openziti/xgress-terminator-half-close-test
Cover an xgress terminator half-closing to a legacy edge client
2026-09-03 21:34:56 -04:00
Paul Lorenz 040942f938 Merge pull request #4315 from openziti/fix/posture-process-list-handling
Fix router posture process-list response handling
2026-09-03 21:33:29 -04:00
Paul Lorenz 49fdd2c0a1 Hold the policy enforcers' meters rather than resolving one per run. Fixes #4350
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.
2026-09-03 17:18:29 -04:00
Andrew Martinez 41afd70917 fixes openziti/ziti#4318 guard mac and domain posture checks against unreported data (#4325)
- 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
2026-09-03 11:13:31 -04:00
Paul Lorenz 2815adb601 Cover an xgress terminator half-closing to a legacy edge client. For #4338
- 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
2026-09-02 21:55:53 -04:00
Andrew Martinez e1561aed54 fixes openziti/ziti#4319 os posture check with no versions passes at the router (#4324)
- 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
2026-09-02 18:34:41 -04:00
Andrew Martinez b1454c6d63 fixes openziti/ziti#4320 normalize reported MAC addresses on the router (#4328)
- 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
2026-09-02 18:34:26 -04:00
Clint Dovholuk a6a20db6ec Merge pull request #3832 from openziti/dependabot/docker/dist/docker-images/ziti-controller/all-e76bae1def
Bump openziti/ziti-console-assets from 4.1.2 to 4.5.1 in /dist/docker-images/ziti-controller in the all group across 1 directory
2026-09-02 16:14:01 -04:00
Andrew Martinez 567920d786 go mod tidy (#4327) 2026-09-01 15:50:42 -04:00
Paul Lorenz 05a3ae7660 Fix router posture process-list response handling. Fixes #4314
- 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
2026-09-01 14:22:57 -04:00
Paul Lorenz 1b2ea0c224 Merge pull request #3836 from openziti/oidc-auth-test
OIDC authentication fablab test
2026-08-31 22:39:24 -04:00
Paul Lorenz ec56d6b7ba Merge pull request #4309 from openziti/fix/server-close-listener-test-race
Wait for the hosting side before tearing down the listener-close test
2026-08-31 14:43:54 -04:00
Paul Lorenz d0c9699b3e Assert routers are actually back up after a recovery restart
- 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
2026-08-28 22:16:50 -04:00
Paul Lorenz e8b42b89ae Add OIDC auth fablab test and supporting test infrastructure
- 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
2026-08-28 22:00:01 -04:00
Paul Lorenz 4445da1f8f Wait for the hosting side before tearing down the listener-close test
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.
2026-08-27 14:37:51 -04:00
Andrew Martinez aff1038519 Merge commit from fork
- 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
2026-08-27 13:34:27 -04:00
Andrew Martinez 991ccffbdd fixes openziti/ziti#4222 report isCertExtendable correctly for OIDC cert auth (#4224)
- 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
2026-08-26 15:59:17 -04:00
Andrew Martinez 948735d86c fixes openziti/ziti#4094 accept first-party certs issued by a separat… (#4140)
* fixes openziti/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
2026-08-26 14:07:31 -04:00
Andrew Martinez d47ccaffee fixes openziti/ziti#4304 build flags on the version response (#4305)
- 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
2026-08-25 17:29:08 -04:00
Paul Lorenz 661016508a Merge pull request #4297 from openziti/fix/cost-header-panic
Use the length-checked accessor for the bind cost header
2026-08-24 19:58:08 -04:00
Paul Lorenz 69b75741d9 Use the length-checked accessor for the bind cost header. Fixes #4296
- 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
2026-08-24 16:19:34 -04:00
Paul Lorenz 5749486410 Merge pull request #4200 from openziti/upgrade-test
Add in-place upgrade smoke test
2026-08-24 15:24:36 -04:00
Paul Lorenz cda6b90506 Merge pull request #4288 from openziti/fix/verify-traffic-timeout
Bound ziti ops verify traffic server wait so a failed dial can't hang
2026-08-24 15:23:00 -04:00
Christopher Britton c639518724 Replicate admin MFA removal across HA cluster members (#4284)
* Run admin MFA removal as a single raft command so a lagging follower cannot serve a partial delete
2026-08-24 14:46:51 -04:00
dependabot[bot] 6507b6cfa5 Bump openziti/ziti-console-assets
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>
2026-08-24 15:23:01 +00:00
Paul Lorenz 804f55abb3 Merge pull request #4218 from openziti/issue-4202
Don't require a client cert on TLS layers that don't request one. Fixes #4202
2026-08-22 09:03:03 -04:00
Paul Lorenz 1f4a7c0c04 Fix ziti ops verify traffic hanging when a dial fails
- 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
2026-08-21 18:27:32 -04:00
Paul Lorenz ba12b60baa Add in-place upgrade smoke test. Fixes #4199
- 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
2026-08-21 18:17:08 -04:00
Paul Lorenz 92cd4bbd57 Merge pull request #4287 from openziti/no-forward-crypto-method
Stop forwarding the crypto method header to hosting SDKs
2026-08-21 16:41:09 -04:00
Paul Lorenz 745d79dd00 Merge pull request #4285 from openziti/update-deps-changelog
Update deps and changelog
v2.1.0-pre2
2026-08-21 16:39:02 -04:00
Paul Lorenz a3e2c92ecf Stop forwarding the crypto method header to hosting SDKs
- 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
2026-08-21 16:12:08 -04:00
Paul Lorenz 820dbe884f Update deps and changelog
- 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
2026-08-21 15:07:30 -04:00
Paul Lorenz a4200c5f5f Merge pull request #4286 from openziti/fix-ext-jwt-overlapping-kids-test
Fix ext-jwt overlapping-kids test under the hardened JWKS resolver
2026-08-21 14:56:15 -04:00
Paul Lorenz a404a23af5 Fix ext-jwt overlapping-kids test under the hardened JWKS resolver
- 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
2026-08-21 11:20:46 -04:00
Paul Lorenz c99e1b8483 Merge commit from fork
fixes GHSA-354c-gpg9-j988 router MFA posture check panics on nil wake/unlock state
2026-08-20 15:41:33 -04:00
Paul Lorenz f940c81ebe Merge commit from fork
Validate the API session token when creating circuits via CreateCircuitV3 (main / 2.1.0)
2026-08-20 15:40:50 -04:00
Andrew Martinez f11240c294 fixes GHSA-354c-gpg9-j988 router MFA posture check panics on nil wake/unlock state
- 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
2026-08-20 15:32:59 -04:00
Paul Lorenz 464232717e Validate the API session token when creating circuits via CreateCircuitV3
- 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
2026-08-20 15:32:58 -04:00
Paul Lorenz e38970803c Merge commit from fork
fixes GHSA-whjr-3j94-gw3c constrain external jwt signer JWKS fetching
2026-08-20 15:27:56 -04:00
Paul Lorenz ef9ffc8a20 Merge commit from fork
fixes GHSA-4h58-w989-xgg4 enforce issuer and audience in ext-jwt toke…
2026-08-20 15:26:13 -04:00
Andrew Martinez 9cf98a1dc9 fixes GHSA-4h58-w989-xgg4 enforce issuer and audience in ext-jwt token enrollment
- 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
2026-08-20 15:15:26 -04:00
Andrew Martinez f64b6879cf fixes GHSA-whjr-3j94-gw3c constrain external jwt signer JWKS fetching
- 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
2026-08-20 15:12:56 -04:00
Paul Lorenz d170b512af Merge commit from fork
adds regression test for api session certificate scoping
2026-08-20 14:46:31 -04:00
Paul Lorenz f53d975f9e Merge commit from fork
fixes GHSA-j952-6x8x-jmj6 reuse buffered request body in legacy enrollment
2026-08-20 14:44:40 -04:00