Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: coder/coder-desktop-macos
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.7.2
Choose a base ref
...
head repository: coder/coder-desktop-macos
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
  • 10 commits
  • 41 files changed
  • 1 contributor

Commits on Aug 6, 2025

  1. chore: make helper launchdaemon approval mandatory (#205)

    First step in addressing #201.
    
    This PR installs and kickstarts the LaunchDaemon as part of the `.pkg` installer, instead of requiring it be approved via the UI. As such, we no longer distribute the app contained within a `.zip`.
    
    This PR adds a build script to install the LaunchDaemon when developing locally, to ensure it stays up to date when changes are made. Installing the LaunchDaemon requires administrator privileges, so to minimise password prompts, we only restart it when the binary itself, or any of it's frameworks have changed.
    
    There's an Apple Developer Forum thread where I replied, enquiring about this installer approach vs the existing SMAppService approach, esp. w.r.t deploying the app via MDM: https://developer.apple.com/forums/thread/766351?answerId=850675022&page=1#851913022
    
    
    (This PR previously had UI changes, and I did some refactoring. That refactoring is still in the diff.)
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    99c912b View commit details
    Browse the repository at this point in the history
  2. chore: run coder connect networking from launchdaemon (#203)

    Continues to address #201.
    
    This PR reworks all XPC connections, such that the networking code runs within the privileged helper, instead of the network extension.
    
    The XPC interfaces are described in `XPC.swift`, and roughly follow this sequence diagram: 
    (One difference is that we don't posix spawn the tunnel in this PR)
    ```mermaid
    sequenceDiagram
        note left of App: User requests to start VPN:
        App->>+NetExt: Start VPN
        NetExt->>+PrivHelper: Request start VPN with TUN FD
        note right of PrivHelper: Privileged helper downloads and verifies binary.
        PrivHelper->>Tunnel: posix_spawn child process with FDs
        PrivHelper->>+Tunnel: Send proto start request
        Tunnel-->>-PrivHelper: Send proto start response
        PrivHelper->>+NetExt: Request for network config change
        NetExt-->>-PrivHelper: Response for network config change
        PrivHelper-->>-NetExt: Start VPN respons
        NetExt-->>-App: VPN started
        App->>PrivHelper: Request peer state
        PrivHelper->>Tunnel: Request peer state
        Tunnel-->>PrivHelper: Peer state response
        PrivHelper-->>App: Peer state response
    
        note left of App: Tunnel updates (bypass NetExt):
        Tunnel->>PrivHelper: Tunnel update proto message
        PrivHelper->>App: Tunnel update proto message
    
        note left of App: User requests to stop VPN:
        App->>+NetExt: Stop VPN
        NetExt->>+PrivHelper: Request stop VPN
        PrivHelper->>+Tunnel: Request stop VPN
        Tunnel-->>-PrivHelper: Stop VPN response
        note right of Tunnel: Tunnel binary exits
        PrivHelper-->>-NetExt: Stop VPN response
        NetExt-->>-App: VPN stopped
    
    ```
    
    Of note is that the network extension starts and stops the daemon running within the privileged helper.
    This is to support starting and stopping the VPN from the toggle in System Settings, and to ensure the "Connecting" and "Disconnecting" phase of the system VPN is indicative of the time the VPN is actually setting itself up and tearing itself down.
    
    To accomplish this, the privileged helper listens on two different service names. One is connected to by the app, the other the network extension. (Once an XPC listener is connected to, communication is bidirectional)
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    8533b31 View commit details
    Browse the repository at this point in the history
  3. fix: add code signing requirements to xpc connections (#206)

    Continues to address #201.
    
    I've manually tested that this change prevents binaries not signed by the Coder Apple development team from connecting to the Helper over XPC.
    
    Most of the PR diff is me moving the validator out of `Download.swift` and into `Validate.swift`
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    ff169e3 View commit details
    Browse the repository at this point in the history
  4. chore: use slim binary over dylib (#210)

    Closes #201.
    
    This PR:
    - Replaces the dylib download with a slim binary download.
    - Removes signature validation checks that are inappropriate for the slim binary.
    - Replaces `TunnelHandle` with `TunnelDaemon`, an abstraction which runs `posix_spawn` on the slim binary, and manages the spawned process.
    - Adds tests for `TunnelDaemon`.
    
    
    In a future PR:
    - Bump the minimum server version for Coder Desktop for macOS (to v2.25 once it's released)
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    8c08563 View commit details
    Browse the repository at this point in the history
  5. chore: ensure downloaded slim binary version matches server (#211)

    Relates to #201.
    
    **After we've validated the binary signature**, we exec `coder version --output=json` to validate the version of the downloaded binary matches the server. This is done to prevent against downgrade attacks, and to match the checking we had on the dylib before.
    
    
    Additionally, this PR also ensures the certificate used to sign the binary is part of an Apple-issued certificate chain.
    
    I assumed we were checking this before (by default) but we weren't. 
    Though we weren't previously checking it, we were only ever downloading and executing a dylib. 
    My understanding is that macOS won't execute a dylib unless the executing process and the dylib were signed by the same Apple developer team (at [least in a sandboxed process](https://developer.apple.com/forums/thread/683914), as is the Network Extension).
    
    Only now, when `posix_spawn`ing the slim binary from an unsandboxed LaunchDaemon, is this check absolutely necessary.
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    b74def3 View commit details
    Browse the repository at this point in the history
  6. fix: start coder connect progress indicator immediately (#214)

    With the changes made in #203, it now takes a moment longer to receive the first progress update, when we either start the download (if not already downloaded), or validate the dylib. To address this, the progress indicator will immediately start making progress towards 25%. This prevents it from appearing stuck in what is an expected situation.
    
    
    https://github.com/user-attachments/assets/da57270d-a50b-49ab-9e53-ae02368c71dc
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    8d9f844 View commit details
    Browse the repository at this point in the history
  7. fix: always open app as logged-in user in postinstall script (#216)

    This PR prevents issues like:
    
    <img width="428" height="171" alt="Screenshot 2025-07-30 at 3 57 07 pm" src="/api/flow.js?q=https%3A%2F%2Fgithub.com%2Fcoder%2Fcoder-desktop-macos%2Fcompare%2F%253Ca%2520href%3D"https://github.com/user-attachments/assets/729055b8-bc9b-43fc-9e95-ed9faa384872">https://github.com/user-attachments/assets/729055b8-bc9b-43fc-9e95-ed9faa384872" />
    
    which occur when the app is launched as root. This can happen when the installer scripts are run as root, which is the case when deploying Coder Desktop over MDM. (As a convenience, we re-open the app if it was open before the installer was ran.)
    
    Of note is that on macOS, it is not sufficient to just run open with `sudo -u`, as that does not use the execution context of the user. See https://developer.apple.com/forums/thread/78332
    
    
    Reports of the bug in other programs:
    (with an incorrect solution) https://community.zoom.com/t5/Zoom-Meetings/A-keychain-cannot-be-found-to-stoer-quot-Zoom-quot/m-p/51059 
    https://displaylink.org/forum/showthread.php?p=97176
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    4ba6ca3 View commit details
    Browse the repository at this point in the history
  8. chore: set minimum coder server version to v2.24.3 (#215)

    This requirement is introduced by #210, as the `vpn-daemon` command on the macOS CLI isn't present on prior versions.
    
    (2.24.3 isn't out yet, but when it does come out, it'll be compatible. 2.25 is out, and it's compatible)
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    f5d0741 View commit details
    Browse the repository at this point in the history
  9. feat: support http coder urls (#217)

    Currently, in the Login Form, we reject non-https URLs. When this HTTPS requirement was added, the Coder Desktop app was unable to make HTTP requests, as it did not have `NSArbitraryLoads`. We ended up needing to enable that flag in order to use the Agent API. (`http://workspace.coder:4`)
    
    To support Coder deployments behind VPNs without HTTPS, we can now loosen the requirement. With this PR, the behavior [matches Windows.](https://github.com/coder/coder-desktop-windows/blob/ac22fe4c44dea03b21557b4a0f1e214397bc3ea4/App/Services/CredentialManager.cs#L175-L176)
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    441bff5 View commit details
    Browse the repository at this point in the history
  10. fix: avoid automatically reconfiguring vpn (#218)

    I added this block of code as a convenience, during the time we were using a workaround for the XPC issue that involved deleting and reinserting the network extension when updated. This block of code meant the user didn't need to click back into the tray menu to get the VPN configuration prompt to appear, it would just appear as soon as necessary.
    
    However, this introduces a race. If the app loads the VPN unconfigured view before the task that checks for an existing VPN configuration finishes (this task runs as part of `applicationDidFinishLaunching`), the user will be displayed the prompt to reconfigure, even if a valid configuration already exists.
    
    Since we're not using the aforementioned workaround, this convenience is no longer necessary, and can be removed as the race is more likely to be an issue.
    ethanndickson authored Aug 6, 2025
    Configuration menu
    Copy the full SHA
    de4b0e5 View commit details
    Browse the repository at this point in the history
Loading