Commit Graph
1966 Commits
Author SHA1 Message Date
Devin Carr 18cdfe0a6f Release 2026.10.0 2026.10.0 2026-10-05 08:39:13 -07:00
Miguel da Costa Martins Marcelino 059f86a8dd VULN-141859: Normalize request path when using access rules
With the previous changes made to normalize request paths, we faced some issues:

* Regressions in Next.js route groups: https://github.com/cloudflare/cloudflared/issues/1719
* WordPress trailing slashes: https://github.com/cloudflare/cloudflared/issues/1717

This PR attempts to fix a vulnerability regarding un-normalized request paths using a different approach:
instead of mutating `req.URL.Path` before forwarding to the origin, we compute a cleaned path used only for
ingress rule selection, leaving the original request completely untouched. This separates the security classification decision from request proxying.

Since this can be a breaking change, we are adding it disabled by default and will only enable it in future releases.
2026-09-29 17:51:37 +01:00
Devin Carr ad3c6d1c2d chore: stabilize websocket event tests
Synchronize websocket writes before asserting so test failures stay
on the test goroutine. Also use the context-aware SOCKS dialer in
the origin connection test.
2026-09-28 10:34:00 -07:00
Miguel da Costa Martins Marcelino 725c46361b chore: Retry DNS resolution
Instead of waiting for 10 seconds, implement a retry with backoff. This is to prevent the DNS flakyness we commonly see.
2026-09-28 14:54:07 +00:00
João "Pisco" Fernandes f9676c5856 TUN-10808: Cancel QUIC reads when request bodies close
Interrupt pending QUIC request body reads without closing response writes
Cancel origin requests before closing HTTP/2 responses
Add HTTP/2 and QUIC flow-control regression coverage
2026-09-24 17:19:13 +01:00
João "Pisco" Fernandes 96d39adbc8 Release 2026.9.3 2026.9.3 2026-09-24 16:31:10 +01:00
João "Pisco" Fernandes a44e90b753 chore: Update dependencies
Upgrade OpenTelemetry and OTLP packages
Update gRPC, grpc-gateway, and Go x packages
2026-09-24 16:10:18 +01:00
João "Pisco" Fernandes 54ea1a5e53 Release 2026.9.2 2026.9.2 2026-09-24 14:51:16 +01:00
João "Pisco" Fernandes 67d0c250d7 TUN-10798: Harden protected Quick Tunnel CLI behavior
Redact recipient rules, improve protected tunnel startup output, and reject --allowed-mail for named tunnels.
Use state-specific cookies so concurrent login flows remain independent, with callbacks selecting and clearing only the matching state cookie.
2026-09-24 14:46:10 +01:00
Miguel da Costa Martins Marcelino a20f569675 TUN-10926: Broker JWKS cache expiration
Adds an expiration for cached JWKS. Expired caches refresh even when kid still matches. All retired keys are rejected after expiration.
2026-09-24 14:44:13 +01:00
João "Pisco" Fernandes 8732a55822 chore: Enable --allowed-mail flag and mac runners 2026-09-24 10:52:32 +01:00
Miguel da Costa Martins Marcelino cdcd702aa4 chore: Add upper bounds to supplied emails
This prevents users from abusing `--allowed-mail` policies by limiting the nr. of rules to 100 and defining a max per rule of 320 bytes. Validation errors now identify only the rule position and category, preventing recipient values from appearing in logs. Parsing also stops once limits are exceeded while preserving normalisation and deduplication.
2026-09-23 15:59:32 +00:00
Miguel da Costa Martins Marcelino 07084fd5fd chore: Protect headers before flush
Ensures protected response headers are filtered before `Flush` commits them. This prevents origins from bypassing reserved-cookie and cache-control protections through early flushing.
2026-09-23 15:58:37 +00:00
Miguel da Costa Martins Marcelino ff9cc0e8ac chore: Limit response size
Adding a 1 MiB response limit so we don't accept responses that are too large. Oversized responses now fail before JSON decoding.
2026-09-23 10:32:05 +00:00
Miguel da Costa Martins Marcelino 4518a7e80c chore: Avoid logging request body
Raw response bodies were being logged by quick tunnel provisioner. This avoids logging the raw body, which could contain sensitive data, while preserving structured Worker errors.
2026-09-22 19:56:12 +00:00
Miguel da Costa Martins Marcelino 0b37a912a2 TUN-10805: Strip authentication data before forwarding requests
To properly secure authorized requests, we need to strip any process-local session and authentication-state cookies before forwarding them. On protected responses, we also enforce `Cache-Control: private, no-store` and `Referrer-Policy: no-referrer` directives, to prevent sensitive data from being cached locally or by intermediaries and to block referrer URLs from leaking across requests.

Response headers and cookies for Public Quick Tunnel are preserved.
2026-09-22 15:13:07 +00:00
Renovate 1d21331299 Update gcr.io/distroless/base-debian13:nonroot Docker digest to 0896741
This MR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| gcr.io/distroless/base-debian13 | final | digest | `d199d20` → `0896741` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At 12:00 AM through 04:59 AM and 10:00 PM through 11:59 PM, Monday through Friday (`* 0-4,22-23 * * 1-5`)
  - Only on Sunday and Saturday (`* * * * 0,6`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC44Mi4wIiwidXBkYXRlZEluVmVyIjoiNDQuODIuMCIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->
2026-09-22 14:40:38 +00:00
Renovate 06ef871a72 Update gcr.io/distroless/base-debian13:nonroot-amd64 Docker digest to 65795e7
This MR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| gcr.io/distroless/base-debian13 | final | digest | `1411d9a` → `65795e7` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At 12:00 AM through 04:59 AM and 10:00 PM through 11:59 PM, Monday through Friday (`* 0-4,22-23 * * 1-5`)
  - Only on Sunday and Saturday (`* * * * 0,6`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC44Mi4wIiwidXBkYXRlZEluVmVyIjoiNDQuODIuMCIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->
2026-09-22 15:26:58 +01:00
Alessandro Frigerio 9c49bb94af TUN-10804: Enforce authentication before origin access
This adds a shared authorization boundary for protected Quick Tunnels before ingress selection. Unauthenticated requests are redirected or rejected, while requests with a valid local session continue to the origin after the internal authentication cookie is removed.

Before these changes, we could already login and validate sessions, but the request handler could not allow authenticated requests to continue to the origin. This change adds that authorization decision.

Public Quick Tunnels remain unchanged.
2026-09-21 16:54:54 +01:00
Alessandro Frigerio 07bcac7cd9 TUN-10803: Wire Quick Tunnel callback authorization
Initializes recipient policy, assertion validation, and session management for protected Quick Tunnels.
2026-09-21 15:08:50 +01:00
Alessandro Frigerio 3e1febbda1 TUN-10803: Authorize Quick Tunnel callbacks
Validates callback-bound broker assertions, enforces recipient rules, and issues local sessions before redirecting with HTTP 303. Callback replay is bounded by the short-lived signed artifacts.
2026-09-21 12:28:07 +00:00
Alessandro Frigerio 05d053acec TUN-10803: Add Quick Tunnel recipient policy
## Summary

Adds a process-local recipient policy for normalized exact-email and wildcard-domain rules.
2026-09-21 11:47:36 +01:00
Alessandro Frigerio be3ac12702 TUN-10802: Manage process-local authentication sessions
## Summary

- generate a cryptographically random process-local HMAC-SHA256 session signing key
- issue host-only, Secure, HttpOnly, SameSite=Lax Quick Tunnel session cookies
- cap sessions at the earlier of 4 hours or the Access identity expiry
- validate canonical cookie encoding, signatures, nonces, and expiry
- invalidate sessions across cloudflared restarts through process-local signing keys
- use `github.com/cloudflare/backoff` for tunnel retry jitter while preserving retry limits, retry-forever caps, and grace-period resets

## Security

- session payloads contain only a random nonce and expiry; no visitor identity or recipient rules
- malformed, tampered, expired, and previous-process sessions fail closed
- HMAC signatures are compared in constant time

## Validation

- `make test lint`
- `go test -race ./retry ./quicktunnelauth`
2026-09-17 17:08:56 +00:00
Miguel da Costa Martins Marcelino 4f80fc1c23 Revert "chore: Fix unbounded channel"
This reverts merge request !1952

Reverting this change. This was an unbuffered channel before, which does not mean "unbounded". The channel was effectively blocking until a receiver read the value.
2026-09-17 15:15:52 +00:00
Miguel da Costa Martins Marcelino 8985ebd469 chore: Fix unbounded channel
For the refresh JWKS operation, a worker fiber was reading from a notification channel that had no buffering.

This has two issues. The first issue has to do with memory growth, but the second is more subtle. When we don't supply any bounds, the code needs to be capable to handle concurrent requests and the worker must be able to dedup notifications. While this is what happens now, a future iteration might introduce a bugthat removes the dedup logic.
2026-09-17 08:21:12 +00:00
Alessandro Frigerio a1cb032df2 TUN-10801: Validate quick tunnel broker assertions
## Summary

Validates callback-bound Quick Tunnel broker assertions using the verification keys cached in !1937. The validator accepts only ES256 JWTs from the fixed broker issuer and audience, verifies signatures before claims, enforces the two-minute handoff lifetime and Access identity expiry, binds the callback hostname and browser state, validates and normalizes the verified email, and returns a trusted identity for recipient authorization.

The temporary hostname validator mirrors TUN-10800 and is documented for consolidation after that stack merges.

## Stack

- Depends on: !1937
- Part 2/2: this MR

## Validation

- `make test lint`
- focused `go test -race ./connection` coverage for JWT headers, signatures, claims, and identity binding
2026-09-16 18:30:13 +00:00
Alessandro Frigerio 156742e3ac TUN-10801: Cache quick tunnel broker JWKS
## Summary

Adds the process-local broker JWKS cache used by protected Quick Tunnel assertion validation. Fetches keys only from the fixed broker endpoint with a five-second timeout, refuses redirects, bounds and validates responses, accepts only public P-256 ES256 signing keys, reuses cached keys, and refreshes once for an unknown key ID. Concurrent cache misses and refreshes are deduplicated.

## Stack

- Part 1/2: this MR
- Followed by: `afrigerio/TUN-10801-assertion-validation`

## Validation

- `make test lint`
- focused `go test -race ./connection` coverage for JWKS caching and refresh
2026-09-16 17:26:55 +00:00
Alessandro Frigerio 5539e4590c TUN-10800: Wire Quick Tunnel authentication redirects
Wires protected Quick Tunnel authentication into the request path so unauthenticated requests are intercepted before ingress selection. Broker callbacks are handled locally and fail closed
2026-09-16 16:30:46 +01:00
Alessandro Frigerio a1dd7d16ba chore: Update gRPC to v1.83.1
## Summary

- Updates `google.golang.org/grpc` from `v1.83.0` to `v1.83.1`.
- Fixes [GO-2026-6348](https://pkg.go.dev/vuln/GO-2026-6348), reported by the `vulncheck-boring-make` validation job.
2026-09-16 15:58:41 +01:00
Alessandro Frigerio eeac161daa TUN-10800: Consume authentication callback state
This adds callback handling for the browser-bound Quick Tunnel authentication state, consuming the POST to /.cloudflared/qt-auth/callback that the broker sends after login. The handler accepts a bounded application/x-www-form-urlencoded body containing exactly one state and one assertion, verifies the HMAC-signed state cookie, and binds it to the submitted state. 

It fails closed and clears the state cookie once consumption succeeds. On success, the handler returns the broker assertion and the validated return path for later use.
2026-09-15 13:52:36 +00:00
Alessandro Frigerio 5cf510d7cc TUN-10800: Add browser-bound login state
This adds QuickTunnelAuthStateManager, which issues browser-bound authentication states for protected Quick Tunnels. Each login generates a random state and a signed, host-only cookie that binds the browser to that state. The cookie is Secure, scoped to the callback path, valid for 10 minutes, and uses SameSite=None because the broker's callback is a cross-site POST from login.trycloudflare.com back to .trycloudflare.com. 

The design is stateless: the cookie is signed with a process-local HMAC key rather than tracked server-side, so there's nothing to store or clean up, and state values are verified rather than looked up. Return-path validation rejects unsafe schemes, hosts, and paths to prevent open redirects.
2026-09-15 09:29:48 +00:00
João "Pisco" Fernandes 2c4f9ebd5a ci: Disable macOS builds while runners are unavailable
## Summary
macOS runners are facing issues with bandwidth, therefore, in order to unblock the work of the team we will temporarily disable those builds.
2026-09-14 17:47:29 +01:00
Devin Carr f11dea9cb7 Release 2026.9.1 2026.9.1 2026-09-10 13:52:23 -07:00
Devin Carr 0f222b39d4 chore: Mark transport-loglevel deprecated
Instead of removing the --transport-loglevel flag, we will mark it
as deprecated. It was no longer used in any logging capacity but
removing the flag will cause existing cloudflared clients who set
the flag to fail to start.

The dedicated transport logger was never used for
log emission, so remove its construction and plumbing through tunnel
configuration, the supervisor, and the connection observer.
2026-09-10 10:00:09 -07:00
Miguel da Costa Martins Marcelino 07b8a87fc3 chore: Add notes on breaking changes
Adding notes on breaking changes to our README.md and AGENTS.md. We should not introduce breaking changes without informing users.
2026-09-10 16:03:57 +00:00
Miguel da Costa Martins Marcelino 232ff3777c Revert "chore: Cleanup unused transport log level"
Reverting this change. Removing flags must be done 3 months in advance. Although this wasn't used, we must announce it before removing it.
2026-09-10 15:05:52 +00:00
Devin Carr b749dfd998 Release 2026.9.0 2026.9.0 2026-09-09 08:55:05 -07:00
Devin Carr a78eeecb56 chore: Cleanup unused transport log level
Remove the unused --transport-loglevel flag and its legacy aliases and
environment variables. The dedicated transport logger was never used for
log emission, so remove its construction and plumbing through tunnel
configuration, the supervisor, and the connection observer.
2026-09-08 17:11:49 -07:00
João Oliveirinha 1ab229d1dd TUN-10738: Propagate edge registration errors over QUIC and fix supervisor error classification
QUIC previously replaced control-stream registration failures with generic
service errors, preventing the supervisor from identifying edge responses such
as EDUPCONN and retryable registration failures. This could repeatedly select
the same edge address or terminate initial registration unexpectedly.

Preserve underlying causes through QUIC service wrappers, including failures
opening the control stream, while returning context cancellation directly.
Represent duplicate registration with a sentinel and use errors.Is and
errors.AsType to classify wrapped errors consistently.

Keep registration permanence available to the supervisor, rotate edge
addresses after EDUPCONN, and classify QUIC dial errors before nested idle
timeouts so connectivity retries can still trigger protocol fallback. Remove
duplicate connection-error logging and suppress expected shutdown noise.

Add coverage for duplicate registration mapping, QUIC error propagation,
cancellation, startup retry policy, edge-address rotation, and wrapped QUIC
dial timeouts.
2026-09-08 14:07:10 -07:00
Renovate da1991cba4 Update gcr.io/distroless/base-debian13:nonroot Docker digest to d199d20
This MR contains the following updates:

| Package | Type | Update | Change |
|---------|------|--------|--------|
| gcr.io/distroless/base-debian13 | final | digest | `97b9d04` → `d199d20` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At 12:00 AM through 04:59 AM and 10:00 PM through 11:59 PM, Monday through Friday (`* 0-4,22-23 * * 1-5`)
  - Only on Sunday and Saturday (`* * * * 0,6`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻️ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

- [ ] If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC40Ni4yIiwidXBkYXRlZEluVmVyIjoiNDQuNDYuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->
2026-09-08 07:38:16 +00:00
Renovate 80c9a54066 Update gcr.io/distroless/base-debian13:nonroot-amd64 Docker digest to 1411d9a
This MR contains the following updates:

| Package | Type | Update | Change |
|---------|------|--------|--------|
| gcr.io/distroless/base-debian13 | final | digest | `a9fb022` → `1411d9a` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At 12:00 AM through 04:59 AM and 10:00 PM through 11:59 PM, Monday through Friday (`* 0-4,22-23 * * 1-5`)
  - Only on Sunday and Saturday (`* * * * 0,6`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻️ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

- [ ] If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC40Ni4yIiwidXBkYXRlZEluVmVyIjoiNDQuNDYuMiIsInRhcmdldEJyYW5jaCI6Im1hc3RlciIsImxhYmVscyI6W119-->
2026-09-08 07:37:56 +00:00
Devin Carr 2253eeeb25 TUN-10834: Bump Go toolchains to 1.26.8
Update CI and standard container builders to Go 1.26.8.

Add coverage for Go 1.26's stricter net/url host parsing, rejecting
unbracketed IPv6 addresses and repeated port separators in origin URLs.
Retain existing Access bare-IPv6 normalization behavior.

Closes TUN-10834
2026-09-03 15:24:38 -07:00
Alessandro Frigerio 79c7547d81 TUN-10799: Negotiate protected mode during Quick Tunnel provisioning
## Summary

Lands the request and data plumbing needed for protected Quick Tunnel provisioning.

This is intentionally staged dead code: `--allowed-mail` is not registered yet, so protected provisioning is not user-reachable in this MR. The existing public Quick Tunnel path remains unchanged. CLI registration and release notes will land when the feature becomes user-reachable.

## Changes

- Add `auth_mode` constants for protected quick tunnels
- Add `buildQuickTunnelRequestBody` helper to construct the OTP request body
- Read `--allowed-mail` values and compute `isProtected` once the flag is registered
- Send `{"auth_mode":"otp"}` when protected mode is requested
- Set `TunnelProperties.IsProtected` for downstream auth handlers
- Preserve structured Worker errors for non-2xx and unsuccessful responses
- Add unit tests for public and protected request bodies
- Add a TODO for CLI-level provisioning-error coverage under TUN-10791

## Validation

- `make test lint`
- Built the `cloudflared` binary locally
- Confirmed `--allowed-mail` is currently rejected as an undefined flag
- Confirmed a public invocation sends a bodyless provisioning request to a mock service
- Confirmed the mock Worker's `503` / `[1234] expected test failure` response is shown by the CLI

## Acceptance criteria

- [x] Protected request construction sends only the supported authentication mode
- [x] Public provisioning request remains bodyless
- [x] Unit tests cover public and protected request bodies
- [x] Current staged CLI behavior manually verified
- [x] Register `--allowed-mail` and make protected mode user-reachable (follow-up)
- [ ] Full CLI/integration coverage under TUN-10791

## Related

- Epic: RM-25152
- Previous/foundation work: TUN-10798
- End-to-end coverage: TUN-10791
2026-09-03 13:03:59 +00:00
Devin Carr 97461b69e6 TUN-10829: Remove vendoring
Remove the tracked vendor directory and build cloudflared from the locked
Go module graph instead.

- Use read-only module mode for build, test, vet, formatting, and linting.
- Configure CI and Docker builders to use Athens with public-proxy and VCS
  fallback.
- Download modules in a separate Docker layer for reusable image builds.
- Remove obsolete module checksums and add the missing shell interpreter to
  the FIPS verification script.

Closes TUN-10829
2026-09-02 16:21:50 -07:00
James Royal d6b4ff083b AUTH-8883 Fix Access token lock self-deadlock during same-process reauth
cloudflared access tcp can fetch an Access token, receive a later Access 302 during WebSocket setup, remove the app token, and immediately fetch again in the same process. The token lock files were intentionally left on disk for stale-PID recovery, but that meant the second fetch saw the first fetch’s lock, recognized its own PID as alive, and waited until lock timeout.

This change restores normal lock release without losing crash recovery. Lock acquisition now returns an ownership handle with a per-acquisition ID, and getToken() releases both app and org token locks on return. Release only removes the lock if PID, process start time, and acquisition agree.

I tested this by manually building and running through a few scenarios on my local machine. So far I haven't seen any lockfiles get left behind and the normal CLI flows are working as expected. 

Closes AUTH-8883
2026-09-02 18:04:04 +00:00
Alessandro Frigerio 9e13f5d300 fix(ci): use fresh clone for Windows jobs
## Problem

Windows CI jobs frequently fail during the `get_sources` phase with hundreds of `Permission denied` warnings while GitLab tries to clean read-only files from `.cache/go/pkg/mod/` left by previous builds. The failure occurs before the build script runs.

Example failed job: https://gitlab.cfdata.org/cloudflare/tun/cloudflared/-/jobs/40007572

## Fix

Set `GIT_STRATEGY: clone` on the shared `.windows-build-defaults` anchor so Windows jobs always start from a fresh clone, bypassing the broken cleanup.

## Related

- MR !1927 (affected by this issue)
2026-09-01 16:56:12 +00:00
João "Pisco" Fernandes fe70e951a3 Release 2026.8.3 2026.8.3 2026-08-31 10:48:10 +01:00
Alessandro Frigerio 274888eb1e TUN-10798: Parse Quick Tunnel allowed mail rules
## Summary

- extract the existing recipient-rule parser and validation helpers from the OTP PoC
- normalize and deduplicate repeated or comma-separated exact email and `*@domain` rules
- reject malformed addresses, wildcard domains, and empty rule sets
- keep the implementation as dead code with no CLI flag, provisioning, auth handler, or transport wiring

## Testing

- `make test`
- `make lint`

## Jira

- [TUN-10798](https://jira.cfdata.org/browse/TUN-10798)
2026-08-28 15:37:26 +00:00
Hugo Vicente 4dd589fedd chore: Remove stale metrics timeout TODO
Removes the stale metrics timeout TODO. Go 1.22+ already adjusts pprof write deadlines for the requested profile duration.

Addresses: [#1733](https://github.com/cloudflare/cloudflared/issues/1733)
2026-08-28 14:37:05 +01:00
Devin Carr 0355484d9b TUN-10822: Remove fetching protocol percentage from remote
Stop fetching protocol rollout percentages from DNS TXT records.
Remove the obsolete remote selector, percentage-fetching API, and
related tests, and simplify protocol selector construction.
2026-08-27 13:29:13 -07:00