Local Network permission prompt for daemon on macOS 15

Hi Team,

OS is prompting for local network permission for our application which runs as root level daemon.

As per the our analysis, it looks like it is prompting from our own library which is trying to get network info ' using /usr/sbin/system_profiler with "-xml -detailLevel basic SPNetworkDataType" and then trying to iterate to find DNS.ServerAddresses for each item. Then using [NSHost hostWithAddress:IPAddress];(When this library is not linked to the app then there is no prompt, so most likely this is the code that is resulting in the prompt).

Is this expected ? . Is there any other way that we can get DNS host name without being prompted for local network permission on mac OS 15

Answered by DTS Engineer in 812072022

We believe this is fixed in macOS 15.1. Please try it out there and let us know otherwise.

Oh, and we just published TN3179 Understanding local network privacy, full of detailed info about local network privacy.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

In general, a daemon should be exempt from local network privacy checks. Before I sent you off to file a bug, I want to confirm one thing. You wrote:

root level daemon.

How is this daemon started? As a launchd daemon? With a property list in /Library/LaunchDaemons? Or something else?

Also, please make sure you’re testing on the macOS 15.0 release. macOS 15 had some late changes in this space. I think all the changes were in the release candidate, but it’s best to test with the release version now that it’s available.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

It is with a property list in /Library/LaunchDaemons, We are seeing the alert on macOS 15.0 (24A335) also.

It is with a property list in /Library/LaunchDaemons and during installation post install script, it is launched using below command. (In actual instead of APP, company it is the name of the app and company , just modified to paste it here) sudo /bin/bash -c "(/bin/sleep 5; $log 'Launching the APP!!!’; /bin/launchctl load -F /Library/LaunchDaemons/com.company.APP.launchdaemon.plist | $log; $log 'Finished launching the APP’) &". I have removed the dylib with reference to NSHOST, now it prompts from the code that uses websockets library(which communicates over localhost/127.0.0.0) . Is this expected? We are seeing the alert on macOS 15.0 (24A335) also.

I suspect that you’re triggering a known bug in the interaction between local network privacy and DNS (r. 133953401). In theory, a launchd daemon running as root should always be allowed to use the local network. In practice, there’s a bug in the DNS infrastructure that means that it can be blocked )-:

I can’t see any workaround for this. All I can say is:

  • This is not fixed in the current macOS 15.1b4 beta seed.

  • As alway, you should test with macOS beta releases as we seed them.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Are there any updates on when this will be fixed?

We believe this is fixed in macOS 15.1. Please try it out there and let us know otherwise.

Oh, and we just published TN3179 Understanding local network privacy, full of detailed info about local network privacy.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks @DTS Engineer I'm still seeing this issue on latest Mac OS 15.1 beta.

Our app is a dotnet 9 executable called "Agent" that's code signed and has all the networking permissions added in entitlements and it's plist. It has a UUID. It works fine when run from the terminal under my (admin) account but will not connect when run as a launchd service using the same account.

The server responds so it has network access but webrtc setup fails and STUN responses are blocked.

Again - works fine from the terminal.

In our app logs network device discovery code is being blocked:

"Discover: No route to host at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.CreateException(SocketError error, Boolean forAsyncThrow) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.SendToAsync"

  • the docs say that the mdns permission isn't needed to macos

and in console i see

default 16:34:32.099971+0800 nehelper UUID cache miss for Agent default 16:34:32.103499+0800 neagent Failed to find Agent in LaunchServices default 16:34:32.104192+0800 nehelper Failed to find Agent using neagent default 16:34:32.104723+0800 nehelper 0 UUIDs for Agent are already in the cache default 16:34:32.106107+0800 neagent Failed to find Agent in LaunchServices default 16:34:32.106211+0800 nehelper Failed to find Agent using neagent default 16:34:32.106319+0800 nehelper Removing UUIDs for ( Agent ) default 16:34:32.108937+0800 nehelper Setting UUID cache generation to 165 default 16:34:32.109041+0800 nehelper Local network allowed by preference for Agent (Agent), but received prompt. Clearing cached UUIDs and restarting session.

This stuff should just work right?

[Hey hey, our posts cross on the ‘wire’!]

TN3179 should be your guide here. As I make clear in that technote, there’s an importance difference between daemons and agents. A daemon runs in the global login session, typically as root. An agent runs in one or more user login sessions. Daemons are not subject to LNP. Agents are.

So, is the program with this problem a daemon or an agent?

[it] will not connect when run as a launchd service using the same account.

Exactly how is that service set up?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for the extra info. Just FYI, it’s better to a reply as a reply rather than in the comments. Quinn’s Top Ten DevForums Tips explains why.

It's just the application is confusingly called "Agent".

Thanks for clarifying. And yeah, that’s not uncommon (think SNMP agent) and it can be confusing, which is why I wanted to clarify.

Your template launchd property list has a YOUR_USERNAME. What’s that about? Generally daemons run as root.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Our service runs under a user account because it also accesses local devices like cameras and microphones. YOUR_USERNAME gets merged in by the installer. I found that if i install our app to Library/Application Support and run it as root then the networking issue goes away (but no local device access which makes this pointless). If it's installed like it was before on previous macOS iterations - on say the desktop and running with the user account then we get local device access but networking is borked.

Also macOS won't prompt for permissions with root but will with the user account. I'm on latest macOS 15.2 beta.

Also this help system isn't working properly - replies aren't displaying and the UI keeps telling me i'm at a negative word count and responses "must include text in the body"

Our service runs under a user account

Which user account?

macOS supports multiple users. Multiple GUI users can be logged in simultaneously, via Fast User Switching. Multiple GUI users can be simultaneously active, via screen sharing. My experience is that any time a daemon tries to act as a user, things end badly. I talk about this in gory detail in Technote 2083 Daemons and Agents.

Our service runs under a user account because it also accesses local devices like cameras and microphones.

Yowsers! Honestly, I’m surprised you’ve got this far.

You really need to be running this code as an agent rather than a daemon. If you also need daemon functionality — privilege escalation, or a persistent network presence — split that out into a daemon and have the agents call on that functionality via IPC.

While you’re not building a screen sharing product, I encourage you to have a read through this thread: Session, Desktops and login screen. In it, I talk another developer through the Five Stages of Mac Screen Sharing Developer Grief™ (-:


Also this help system isn't working properly

I’m interested in hearing more about your problems, but I’d prefer to keep this thread focused on networking. Please start a new thread over in Developer Tools & Services > Developer Forums and we can chat about this over there.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for responding. "Yowsers! Honestly, I’m surprised you’ve got this far."

Well it was working fine on every version of macOS up to this one. It's also working fine as a service on Windows and every version of Linux - with local device access and networking. It's literally -=only=- Sequoia that there are problems on.

I guess i'll just have to tell people to either use another OS, use Docker or just run it from the terminal.

I don't understand why you make it so difficult to do things that on other OSes is simple. If someone gives an app root access then why can't it access local devices? It doesn't make any sense. And if an app is running as a service under an admin account (my account is an admin account) then why is it ONLY blocking these STUN responses? It's got network access and TCP works fine. That's the main issue here - the OS shouldn't be blocking this specific network traffic from our app - that's definitely a bug.

Our app is cross platform i'm not splitting it into different apps with varying levels of privelege and using IPC just to work around a bug in sequoia.

Our app is a video surveillance platform - it needs to start at system startup not login so running it as a launch agent isn't an option. It doesn't have a normal GUI it runs a web server.

LaunchDaemon plist files have a UserName field for the specific purpose of running a system level daemon with specific user privileges. That's not something i've exploited it's part of your own system design.

Well it was working fine on every version of macOS up to this one.

Such is the nature of undefined behaviour. Something might work today and then fail on some future OS release, or fail on some older OS release that you didn’t test on, or fail on some specific device configurations, or just seem to work and then fail at random. We document these rules [1] with the understand that, if you follow the rules, we will do our best to maintain binary compatibility going forward.

If someone gives an app root access then why can't it access local devices?

It can. That bit works just fine. The issue you’re having is that you’re running in a mixed execution context:

  • If you were running as a daemon, you get local network access without user approval [2].

  • If you were running as an agent, the user would be prompted to grant you local network access.

You’re running as a daemon but pretending to be a user. The system is not set up for that because it’s not a supported configuration.

LaunchDaemon plist files have a UserName field for the specific purpose of running a system level daemon with specific user privileges.

Right. But that’s intended to be used for role accounts, like the _www user. It’s not designed to move your daemon into a user session.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Well, we try to document these rules )-:

[2] On macOS 15.1 and later. There was a gnarly bug with this on 15.0.

OK - i've spent a month trying to get this to work. There's really one outstanding issue and that's that some UDP packets are being blocked when our daemon is running under an admin user account. Can you confirm that that is by design? Are there any workarounds you can think of short of us completely restructuring our application?

"You’re running as a daemon but pretending to be a user. The system is not set up for that because it’s not a supported configuration."

So, i'm sorry but what is the point of the UserName field in the launchd plist file then?

We could run this as root but then we lose the ability to access the camera and microphone which is not ideal for video surveillance.

I just need to ascertain if this is a total dead end or if something is going to be done about it.

The manual documentation for UserName in launch daemon plist files says "This optional key specifies the user to run the job as."

It doesn't say " intended to be used for role accounts, like the _www user. It’s not designed to move your daemon into a user session."

If i ask chatgpt about it it says "In Apple's developer documentation, the <key>UserName</key> field in a launch daemon's property list (.plist) specifies the user account under which the daemon should run. By default, daemons run as the root user, but setting this key allows the daemon to operate with the permissions of a specified user."

I'm really struggling to find where it says anywhere in the developer documentation that what we are doing is "not a supported configuration". From everything i can find on your own docs and forums, what we are doing is exactly how we are supposed to be doing it.

Local Network permission prompt for daemon on macOS 15
 
 
Q