Skip to content

iOS: tunnel extension panics in FileAppender::new when log file cannot be opened (EPERM before first unlock) #6299

Description

@rachyandco

Software (please complete the following information):

  • Operating system: iOS 26.6.1 (Darwin 25.6.0, aarch64)
  • NymVPN version: nym-vpn-lib 2026.12.1-beta.3 (iOS beta)

Describe the bug
The NymMixnetTunnel extension crashes on startup with a Rust panic when the log file cannot be created:

panic: initializing rolling file appender failed: InitError { context: "failed to create log file",
source: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" } }
  nym_vpn_lib::logging::FileAppender::new
  nym_vpn_lib::logging::setup_logging
  _uniffi_nym_vpn_lib_uniffi_fn_func_initlogger

FileAppender::new (nym-vpn-core/crates/nym-vpn-lib/src/logging.rs) uses tracing_appender::rolling::never, which .expect()s on any error while opening the log file, so a logging failure takes down the whole tunnel process.

The EPERM comes from iOS Data Protection. The extension calls initLogger from PacketTunnelProvider.init() with a log dir inside the app-group container. No target sets a default-data-protection entitlement, so files get CompleteUntilFirstUserAuthentication. With the always-on NEOnDemandRuleConnect rule enabled, iOS launches the extension at boot before the device is unlocked, and open() on the protected log file is refused. The directory existed (error context is "failed to create log file", not "... log directory"), only the file open failed.

Sentry: nym-vpnd issue 7462203070.

To Reproduce
Steps to reproduce the behavior:

  1. In the app, enable on-demand / always-on connection.
  2. Reboot the iPhone and leave it locked (do not enter the passcode).
  3. Wait for the on-demand rule to launch the tunnel extension.
  4. Observe the crash in Sentry / device crash logs. The VPN does not come up until the device is unlocked.

Expected behavior
Logger initialisation must never abort the tunnel. If the log file cannot be opened, the extension should log a warning via oslog, continue without the file layer, and optionally reopen the file lazily on first write once the device is unlocked. The Swift-side LogFileManager in the same process already tolerates this case with try?; the Rust side should match.

Screenshots
N/A. Sentry stack trace above.

Additional context

  • Impact: after an overnight iOS update or reboot, auto-connect fails and every on-demand launch attempt is reported to Sentry as a fatal crash until the user unlocks the phone.
  • Proposed fix in nym-vpn-lib:
    1. Make FileAppender::new fallible using RollingFileAppender::builder().build(dir) and return the InitError to setup_logging.
    2. In setup_logging, skip the file layer with a warning instead of panicking.
    3. Optional: reuse FileAppender::refresh to retry opening the file on first write.
    4. Optional: surface the failure through initLogger into the existing PacketTunnelProvider.logInitFailure field.
  • Alternative or complementary: set NSFileProtectionNone / completeUntilFirstUserAuthentication explicitly on the Logs directory from Swift, but the Rust side should still not panic.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions