🔐 Users and security on NetBSD

Add, lock and remove users, understand wheel and su without sudo, read the nightly security report and raise the securelevel. Every example run for real, with the errors you will meet.

Video: Users and security on NetBSD (NetBSD video)
Prefer to watch? This short video runs this page's main commands on a real NetBSD 11.1 machine, with on-screen captions and a synthetic voiceover (an AI voice trained on the author's own recording). Click to play, or watch it on YouTube.
useradd -m -c "Ann Example" ann     # new user with a home
usermod -C yes ann                   # lock the account
sysctl -w kern.securelevel=1         # root loses some powers

NetBSD manages users with a few small commands that edit plain files, has no sudo in the base system, lets only the group wheel use su, mails a nightly report of changes, and has a kernel securelevel that even root cannot lower without a reboot. This page adds, locks and removes users, shows why usermod -G only adds, tries su as a user outside wheel, runs the security report and raises the securelevel.

Every example ran for real on NetBSD 11.1 (amd64) in a virtual machine, with root commands marked #. The output you see is the output the machine gave.

Users and groups, and where they live

A NetBSD system keeps its users in plain files, and a few small commands edit them for you. If you know Linux, the commands will look familiar, and the files too, with one difference: the real password hashes are in a file that only root can read, and a database is built from it.

Every example here ran for real on NetBSD 11.1 (amd64) in a virtual machine. Commands that change the system start with # and run as root, because NetBSD has no sudo by default. First, who are we, and what do the files hold?

$ id
uid=1000(bsd) gid=100(users) groups=100(users),0(wheel)
$ grep '^bsd:' /etc/passwd
bsd:*:1000:100::/home/bsd:/bin/sh
# grep '^bsd:' /etc/master.passwd | cut -c1-24
bsd:$argon2id$v=19$m=102
$ ls -l /etc/passwd /etc/master.passwd /etc/spwd.db /etc/pwd.db
-rw-------  1 root  wheel   1818 Nov  5 11:33 /etc/master.passwd
-rw-r--r--  1 root  wheel   1503 Nov  5 11:33 /etc/passwd
-rw-r--r--  1 root  wheel  40960 Nov  5 11:33 /etc/pwd.db
-rw-------  1 root  wheel  40960 Nov  5 11:33 /etc/spwd.db
$ grep -E '^(wheel|users):' /etc/group
wheel:*:0:root,bsd
users:*:100:

/etc/passwd is readable by everyone and has a * where the password hash would be. The real hash is in /etc/master.passwd, readable by root only, with two more fields (the login class and the password change time). The two .db files are the same data in a form the system can look up quickly, and they are rebuilt for you when you use the user commands. The group wheel is the group of administrators. Notice that the user in this machine is a member of it.

Adding a user

useradd creates the account, and with -m it makes the home directory too, copying the files in /etc/skel. The comment is the full name:

# useradd -m -c "Ann Example" ann
$ id ann
uid=1001(ann) gid=100(users) groups=100(users)
$ grep '^ann:' /etc/passwd
ann:*:1001:100:Ann Example:/home/ann:/bin/sh
$ ls -a /home/ann
.
..
.cshrc
.login
.logout
.profile
.shrc
# userinfo ann
login	ann
passwd	*************
uid	1001
groups	users
change	NEVER
class
gecos	Ann Example
dir	/home/ann
shell	/bin/sh
expire	NEVER

The new user got the next free user id, 1001, the default group users, the shell /bin/sh, and a password field that is a row of stars, which means nobody can log in with a password yet. The defaults come from a small file:

$ cat /etc/usermgmt.conf
group		users
base_dir	/home
skel_dir	/etc/skel
shell		/bin/sh
class
homeperm	0755
inactive	Null (unset)
expire		Null (unset)
preserve	false

Change the shell or the base directory there and every later useradd follows it.

Passwords, locking and shells

To give the account a password, either run passwd ann and type it twice, or give useradd and usermod a ready-made hash with -p. The pwhash command makes the hash. The default algorithm in NetBSD 11 is argon2id, and every run adds a fresh random salt, so you never get the same hash twice:

$ echo hunter2 | pwhash | cut -c1-28
$argon2id$v=19$m=1024,t=48,p
$ echo hunter2 | pwhash | cut -c1-28
$argon2id$v=19$m=1024,t=48,p
# usermod -p "$(echo hunter2 | pwhash)" ann
# grep '^ann:' /etc/master.passwd | cut -c1-30
ann:$argon2id$v=19$m=1024,t=48

To stop an account from logging in without deleting it, lock it. A lock puts *LOCKED* in front of the hash, so the password can be restored with the same command:

# usermod -C yes ann
# grep '^ann:' /etc/master.passwd | cut -c1-30
ann:*LOCKED*$argon2id$v=19$m=1
# usermod -C no ann
# grep '^ann:' /etc/master.passwd | cut -c1-30
ann:$argon2id$v=19$m=1024,t=48

An account that should never log in at all, such as one that only owns files, gets the shell /sbin/nologin:

# usermod -s /sbin/nologin ann
# su ann -c id; echo "exit status $?"
This account is currently not available.
exit status 1
# usermod -s /bin/sh ann

Groups

Groups are made with groupadd, and a user is added to one with usermod -G. Make a group for the people who look after the web site and put Ann in it:

# groupadd -g 2000 web
# usermod -G web ann
$ id ann
uid=1001(ann) gid=100(users) groups=100(users),2000(web)
$ grep '^web:' /etc/group
web:*:2000:ann

Here is the part that surprises people coming from Linux. usermod -G only adds. Give Ann a second group and she keeps the first:

# usermod -G wheel ann
$ id ann
uid=1001(ann) gid=100(users) groups=100(users),0(wheel),2000(web)

There is no option that takes a user out of a group. For that you edit /etc/group, which is a plain text file of one line per group, with the members separated by commas. Here the member is removed with sed (some systems have vigr for this, NetBSD does not), and nothing else needs to be updated, because the file is read as it is:

# sed -i 's/:ann$/:/; s/,ann$//; s/,ann,/,/' /etc/group
$ id ann
uid=1001(ann) gid=100(users) groups=100(users)
$ grep -E '^(wheel|web):' /etc/group
wheel:*:0:root,bsd
web:*:2000:

Be careful with that command. It is easy to match too much, so look at the file afterwards, as above.

wheel and su

On NetBSD only members of the group wheel can become root with su, even if they know the root password. That is a good default, and it means that the first thing to check on a new machine is who is in that group. Try it as a user who is not in it. This command needs a terminal to ask the password on, so the page runs it through ssh -tt, which gives it one, and the command you would type is the one shown:

# useradd -m bob
$ su bob -c "su -"
su: You are not listed in the correct secondary group (wheel) to su root.
su: Sorry: Authentication error
(exit status 1)

The refusal comes before the password is asked, so it tells you nothing about the password. To let Bob use su, add him to wheel, and to take it away again, remove him from the line as in the last section:

# usermod -G wheel bob
$ id bob
uid=1002(bob) gid=100(users) groups=100(users),0(wheel)
# sed -i 's/,bob$//' /etc/group
$ id bob
uid=1002(bob) gid=100(users) groups=100(users)

There is no sudo or doas in the base system. Both are in pkgsrc and can be installed with pkgin, which the earlier page on pkgin and pkgsrc shows. For one or two administrators, su and wheel are often enough.

Changing and removing users

To rename a user or to move the home directory, use usermod -l and usermod -d. To remove a user, userdel. By default it leaves the home directory behind, so the files are not lost by accident, and -r removes it as well:

# userdel bob
$ ls /home
ann
bob
bsd
# rm -rf /home/bob
# userdel -r ann
$ ls /home
bsd
$ id ann
id: ann: No such user
(exit status 1)

Note that userdel does not look for files that the user owns outside the home directory. Files with the user's number stay behind, and a new user who gets the same number would own them. Find them first with find / -user NAME -ls, before you delete the account.

The security report

NetBSD checks itself every night. The daily job runs /etc/security, which compares important files with the copy it made the last time, and mails the differences to root. The same checks can be run by hand. The first run on a new system is long, because it reports every change since the install. It also saves a new copy of each file, so the next run only has news in it. Run it once and throw the output away, then make one change and run it again:

# sh /etc/security > /dev/null 2>&1
# useradd -m carol
# sh /etc/security 2>&1 | grep -A9 '^/etc/passwd diffs'
/etc/passwd diffs (OLD < > NEW)
======
--- /var/backups//etc/passwd.current	2026-11-06 17:28:37.305306659 +0000
+++ /etc/passwd	2026-11-06 17:28:40.358308859 +0000
@@ -25,3 +25,4 @@
 uucp:*:66:1:UNIX-to-UNIX Copy:/nonexistent:/sbin/nologin
 nobody:*:32767:39:Unprivileged user:/nonexistent:/sbin/nologin
 bsd:*:1000:100::/home/bsd:/bin/sh
+carol:*:1001:100::/home/carol:/bin/sh

The new line for carol is shown with a +. This is the cheapest intrusion alarm there is: an unexpected account in the report is worth a look. The nightly job is controlled by a few lines in /etc/daily.conf, and the checks themselves in /etc/security.conf, and the defaults say what each one is:

$ grep -E '^(run_security|separate_security_email|send_empty_security)' /etc/defaults/daily.conf
run_security=YES
separate_security_email=YES
send_empty_security=NO
$ grep -c '^check_' /etc/defaults/security.conf
28

The report goes to root's mail, so it is a good idea to forward that to an address you read.

Securelevel

NetBSD has one more protection that Linux does not have in this form. The kernel runs at a securelevel, a number that can be raised while the system runs and lowered only by rebooting. At level 1 and above, files marked immutable cannot be changed or deleted even by root, kernel modules cannot be loaded, and the memory devices cannot be written. First see the level, then mark a file immutable and raise the level:

$ sysctl kern.securelevel
kern.securelevel = -1
$ touch /tmp/imm
# chflags schg /tmp/imm
$ ls -lo /tmp/imm
-rw-r--r--  1 bsd  wheel  schg 0 Nov  6 17:28 /tmp/imm
# sysctl -w kern.securelevel=1
kern.securelevel: -1 -> 1
$ sysctl kern.securelevel
kern.securelevel = 1

Now root is not all-powerful any more:

# chflags noschg /tmp/imm
chflags: /tmp/imm: Permission denied
(exit status 1)
# rm /tmp/imm
rm: /tmp/imm: Operation not permitted
(exit status 1)
# modload /tmp/x
modload: /tmp/x: Operation not permitted
(exit status 1)
# sysctl -w kern.securelevel=-1
sysctl: kern.securelevel: Operation not permitted
(exit status 1)

All of them are refused, the last one because the level can only go up. The way back is a reboot. To raise the level at every boot, put the line securelevel=1 in /etc/rc.conf. The default is empty, so the level stays at -1:

$ grep '^securelevel' /etc/defaults/rc.conf
securelevel=""					# securelevel to set to

Use it on a machine where the important files, such as the kernel and /sbin, are marked immutable, and where you do not need to change the clock, create disk partitions or add devices while it is running. A desktop is a poor place for it, and a firewall or a web server is a good one.

When it goes wrong

The messages are short, but they mean exactly what they say. Creating a user or a group that already exists, or removing one that does not:

# useradd carol; echo "exit status $?"
useradd: Can't add user `carol': `carol' is already a user
exit status 1
# groupadd web; echo "exit status $?"
groupadd: Can't create group `web': already exists
exit status 1
# userdel nobody2; echo "exit status $?"
userdel: No such user `nobody2'
exit status 1

Locking an account that is already locked, which is harmless, and changing a user that does not exist:

# usermod -C yes carol; usermod -C yes carol; echo "exit status $?"
usermod: Account is already locked
exit status 0
# usermod -s /sbin/nologin nobody2; echo "exit status $?"
usermod: Can't modify user `nobody2': no such user
exit status 1

Setting a shell that is not listed in /etc/shells does work for usermod, but ftp and some other services will refuse the user, so check the list when a login works and a transfer does not:

$ grep -v -e '^#' -e '^$' /etc/shells | head -n 3
/bin/sh
/bin/csh
/bin/ksh

And the securelevel one, which is the one to remember before you raise it on a machine you can only reach over the network: you can get back in, but you cannot go back down.