FuguLib::Privdrop(3p) Perl Library Manual FuguLib::Privdrop(3p)

FuguLib::Privdropdrop root privileges permanently

use FuguLib::Privdrop;

FuguLib::Privdrop->drop_privileges(user => '_openhap');

FuguLib::Privdrop implements the privilege drop an OpenBSD daemon performs once its privileged work is done: bind the reserved port, fork the helper that needs root, then give up root for good and run the event loop as an unprivileged user.

The module keeps no state and has a single class method.

(%args) switches the process to the given user and group and verifies that root cannot be regained.

The arguments are as follows:

user
Name of the user to become. Required.
group
Name of the group to become. Optional; the user's primary group is used if it is omitted.

The method returns immediately if the effective user ID is not 0, so a program that is already unprivileged may call it unconditionally. Otherwise it resolves the user and group, calls setgid(2) before setuid(2), assigns the real and effective IDs, and then confirms both that neither ID is 0 any more and that calling setuid(2) with 0 does not bring root back.

drop_privileges() returns 1 on success, including the case where the process was already unprivileged. It never returns on failure.

Bind a reserved port as root, then run as _myapp:

my $socket = IO::Socket::INET->new(
    LocalPort => 80,
    ReuseAddr => 1,
    Listen    => SOMAXCONN,
) or die "Cannot bind port 80: $!";

FuguLib::Privdrop->drop_privileges(user => '_myapp');

while (my $client = $socket->accept) {
    handle_client($client);
}

drop_privileges() dies, rather than returning a failure indication, when user is missing, when the user or group cannot be resolved, when setgid(2) or setuid(2) fails, or when the process still holds root afterwards. A privilege drop that half succeeded is not something a caller can be allowed to continue past.

setgid(2), setuid(2), FuguLib::Daemon(3p), FuguLib::Process(3p)

Dick Olsson <hi@senzilla.io>

Supplementary groups are not changed. A process that was started as root keeps whatever supplementary groups it inherited, which on OpenBSD is how a daemon retains access to the mdnsd(8) socket after dropping to its own user. Callers that need the groups cleared must call setgroups(2) themselves before dropping.

July 27, 2026 OpenBSD