In the last post, we’ve initially configured FreeBSD so that networking is up and running, additional packages can be installed, and the system was patched to the latest release. In this post we’ll look at how users and groups are managed in FreeBSD and what FreeBSD provides when it comes to additional security mechanisms.
On Linux systems users are usually created with useradd, and groups are created with groupadd. There is also usermod and groupmod, which are used to modify users and groups. With FreeBSD you can do all those tasks with pw, no matter if you want to manage users or groups:
1 2 3 | root@freebsd14:~ $ pw usage: pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches /values ] |
By giving the “help” switch to a sub command you may easily check the syntax (or read the man page, of course):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | root@freebsd14:~ $ pw group help usage: pw group [add|del|mod|show|next] [help|switches /values ] root@freebsd14:~ $ pw group add help usage: pw groupadd [group|gid] [switches] -V etcdir alternate /etc location -R rootdir alternate root directory -C config configuration file -q quiet operation -n group group name -g gid group id -M usr1,usr2 add users as group members -o duplicate gid ok -Y update NIS maps -N no update |
A typical group would be created like this:
1 | root@freebsd14:~ $ pw group add -n group1 -g 2001 |
A new user with that group as the primary group would be:
1 2 3 | root@freebsd14:~ $ pw user add -n user1 -u 2001 -c "a sample user" -d /home/user1 -g group1 -m -s /usr/local/bin/bash root@freebsd14:~ $ id -a user1 uid=2001(user1) gid=2001(group1) groups =2001(group1) |
Another option you have is adduser. This is basically a wrapper around “pw” and gives you an interactive way of creating users:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | root@freebsd14:~ $ adduser Username: user2 Full name: second sample user Uid (Leave empty for default): 2002 Login group [user2]: group2 Group group2 does not exist! Login group [user2]: Login group is user2. Invite user2 into other groups ? []: group1 Login class [default]: Shell (sh csh tcsh bash rbash nologin) [sh]: bash Home directory [ /home/user2 ]: Home directory permissions (Leave empty for default): Enable ZFS encryption? ( yes /no ) [no]: Use password-based authentication? [ yes ]: Use an empty password? ( yes /no ) [no]: Use a random password? ( yes /no ) [no]: Enter password: Enter password again: Lock out the account after creation? [no]: Username : user2 Password : ***** Full Name : second sample user Uid : 2002 ZFS dataset : zroot /home/user2 Class : Groups : user2 group1 Home : /home/user2 Home Mode : Shell : /usr/local/bin/bash Locked : no OK? ( yes /no ) [ yes ]: yes adduser: INFO: Successfully created ZFS dataset (zroot /home/user2 ). adduser: INFO: Successfully added (user2) to the user database. Add another user? ( yes /no ) [no]: no Goodbye! root@freebsd14:~ $ id -a user2 uid=2002(user2) gid=2002(user2) groups =2002(user2),2001(group1) |
adduser also created a new ZFS file system for the new user (this did not happen with pw, and this is only done when the parent of the home directory is also ZFS):
1 2 | root@freebsd14:~ $ df -h | grep user zroot /home/user2 24G 128K 24G 0% /home/user2 |
All those defaults can be controlled, as you can ask adduser to create a template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | root@freebsd14:~ $ adduser -C Uid (Leave empty for default): 2003 Login group []: group1 Enter additional groups []: user2 Login class [default]: Shell (sh csh tcsh bash rbash nologin) [sh]: bash Home directory [ /home/ ]: Home directory permissions (Leave empty for default): Enable ZFS encryption? ( yes /no ) [no]: Use password-based authentication? [ yes ]: Use an empty password? ( yes /no ) [no]: Use a random password? ( yes /no ) [no]: Lock out the account after creation? [no]: Pass Type : yes Class : Groups : group1 user2 Home : /home/ Home Mode : Shell : /usr/local/bin/bash Locked : no OK? ( yes /no ) [ yes ]: yes Re-edit the default configuration? ( yes /no ) [no]: Goodbye! |
This created “/etc/adduser.conf”, which will be the template for new users created with adduser:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | root@freebsd14:~ $ cat /etc/adduser .conf # Configuration file for adduser(8). # NOTE: only *some* variables are saved. # Last Modified on Tue Nov 26 16:04:34 CET 2024. defaultHomePerm= defaultLgroup=group1 defaultclass= defaultgroups=user2 passwdtype= yes homeprefix= /home defaultshell= /usr/local/bin/bash udotdir= /usr/share/skel msgfile= /etc/adduser .msg disableflag= uidstart=2003 |
So far there is nothing special, the commands are not the same as on Linux, but the concepts are very similar. What you might have noticed is, that there is something which is called a “login class”. Login classes are used to setup users environments and optionally put restrictions on resource usage. Those classes are defined in “/etc/login.conf” and the default class looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | default:\ :passwd_format=sha512:\ :copyright=/etc/COPYRIGHT:\ :welcome=/var/run/motd:\ :setenv=BLOCKSIZE=K:\ :mail=/var/mail/$:\ :path=/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin ~/bin:\ :nologin=/var/run/nologin:\ :cputime=unlimited:\ :datasize=unlimited:\ :stacksize=unlimited:\ :memorylocked=64K:\ :memoryuse=unlimited:\ :filesize=unlimited:\ :coredumpsize=unlimited:\ :openfiles=unlimited:\ :maxproc=unlimited:\ :sbsize=unlimited:\ :vmemoryuse=unlimited:\ :swapuse=unlimited:\ :pseudoterminals=unlimited:\ :kqueues=unlimited:\ :umtxp=unlimited:\ :priority=0:\ :ignoretime@:\ :umask=022:\ :charset=UTF-8:\ :lang=C.UTF-8: |
There are many more examples in that file, take your time and have a look at them to get an idea. If you change something in that file, you’ll need to re.generate the database with “cap_mkdb“. There is the same concept for the password files. If you check “/etc/master.passwd” you see the hashed passwords (in Linux it is /etc/shadow), but if you check “/etc/passwd” there are no hashes anymore:
1 2 3 4 5 6 7 8 9 | root@freebsd14:~ $ egrep "^root|^user1|^user2" /etc/master . passwd root:$6$rwisdlYvWBHh7XE/$UjQ7zuTqdDXKSjxCjNph6KBWAn.S5lfwal4FsZGWBJsKDsvbfWSJ3asp7BOa9o09iRVNWrpgXKqxh2J9RnUZs/:0:0::0:0:Charlie &: /root : /bin/sh user1:*:2001:2001::0:0:a sample user: /home/user1 : /usr/local/bin/bash user2:$6$HdjGHoWTrlJChtkn$dLkcSNPn8.O98 /rjm91GhGM7lxHb1rrumK0 .SSXtO. /5jr0LqddyG7Es8ijqVuge9cDdmwz0BF3q5uGq7ERDn/ :2002:2002::0:0:second sample user: /home/user2 : /usr/local/bin/bash root@freebsd14:~ $ egrep "^root|^user1|^user2" /etc/passwd root:*:0:0:Charlie &: /root : /bin/sh user1:*:2001:2001:a sample user: /home/user1 : /usr/local/bin/bash user2:*:2002:2002:second sample user: /home/user2 : /usr/local/bin/bash |
The reason is that normal users are not allowed to read “/etc/master.passwd” but still can get basic account information out of “/etc/passwd”. So there needs to be a way to generate the second one out of the first one, and this is done with “pwd_mkdb“. In the same way as “cap_mkdb” does it for the login classes, this one does it for the password databases:
1 2 3 | root@freebsd14:~ $ ls -l /etc/ * pwd *.db -rw-r--r-- 1 root wheel 40960 Nov 26 15:58 /etc/pwd .db -rw------- 1 root wheel 40960 Nov 26 15:58 /etc/spwd .db |
The result are binaries and faster to process than the text representation of the base files. In addition the “/etc/passwd” file is created out of the master file without the sensitive information.
Let’s do a simple test show how this works. The password database files currently have these timestamps:
1 2 3 4 5 6 | root@freebsd14:~ $ ls -la /etc/ * pwd *.db -rw-r--r-- 1 root wheel 40960 Nov 27 14:06 /etc/pwd .db -rw------- 1 root wheel 40960 Nov 27 14:06 /etc/spwd .db root@freebsd14:~ $ ls -la /etc/ * passwd * -rw------- 1 root wheel 2124 Nov 27 14:01 /etc/master . passwd -rw-r--r-- 1 root wheel 1781 Nov 26 15:58 /etc/passwd |
Changing the shell of “user2” using “pw” will update all the files at once:
1 2 3 4 5 6 7 8 9 10 11 | root@freebsd14:~ $ grep user2 /etc/passwd user2:*:2002:2002:second sample user: /home/user2 : /usr/local/bin/bash root@freebsd14:~ $ pw user mod user2 -s /bin/sh root@freebsd14:~ $ grep user2 /etc/master . passwd user2:$6$HdjGHoWTrlJChtkn$dLkcSNPn8.O98 /rjm91GhGM7lxHb1rrumK0 .SSXtO. /5jr0LqddyG7Es8ijqVuge9cDdmwz0BF3q5uGq7ERDn/ :2002:2002::0:0:second sample user: /home/user2 : /bin/sh root@freebsd14:~ $ ls -la /etc/ * passwd * -rw------- 1 root wheel 2112 Nov 27 14:16 /etc/master . passwd -rw-r--r-- 1 root wheel 1757 Nov 27 14:16 /etc/passwd root@freebsd14:~ $ ls -la /etc/ * pwd *.db -rw-r--r-- 1 root wheel 40960 Nov 27 14:16 /etc/pwd .db -rw------- 1 root wheel 40960 Nov 27 14:16 /etc/spwd .db |
On the other hand, if we change the shell of “user2” back to bash manually in “/etc/master.passwd”:
1 2 3 | # open, change manually back to /usr/local/bin/bash and save root@freebsd14:~ $ grep user2 /etc/master . passwd user2:$6$HdjGHoWTrlJChtkn$dLkcSNPn8.O98 /rjm91GhGM7lxHb1rrumK0 .SSXtO. /5jr0LqddyG7Es8ijqVuge9cDdmwz0BF3q5uGq7ERDn/ :2002:2002::0:0:second sample user: /home/user2 : /usr/local/bin/bash |
… the other files have not been touched and still show the old timestamp:
1 2 3 4 5 6 | root@freebsd14:~ $ ls -l /etc/ * pwd * -rw-r--r-- 1 root wheel 40960 Nov 27 14:16 /etc/pwd .db -rw------- 1 root wheel 40960 Nov 27 14:16 /etc/spwd .db root@freebsd14:~ $ ls -l /etc/ * passwd * -rw------- 1 root wheel 2124 Nov 27 14:47 /etc/master . passwd -rw-r--r-- 1 root wheel 1757 Nov 27 14:16 /etc/passwd |
In this case we need to run “pwd_mkdb” manually to get this done:
1 2 3 4 5 6 7 8 9 | root@freebsd14:~ $ pwd_mkdb -p /etc/master . passwd root@freebsd14:~ $ ls -l /etc/ * pwd * -rw-r--r-- 1 root wheel 40960 Nov 27 14:52 /etc/pwd .db -rw------- 1 root wheel 40960 Nov 27 14:52 /etc/spwd .db root@freebsd14:~ $ ls -l /etc/ * passwd * -rw------- 1 root wheel 2124 Nov 27 14:47 /etc/master . passwd -rw-r--r-- 1 root wheel 1769 Nov 27 14:52 /etc/passwd root@freebsd14:~ $ grep user2 /etc/passwd user2:*:2002:2002:second sample user: /home/user2 : /usr/local/bin/bash |
At least from my point of view it is not advisable to do it like this. If you want to edit users manually, instead of manually editing the master file you should use “vipw“. This at least does some sanity checks for you and is automatically rebuilding the password databases. Otherwise us “pw” or “chpass“.
The final topic for this post is something that very much reminds of PostgreSQL’s pg_hba.conf, which is called “login access control table” in FreeBSD. As in PostgreSQL, you can define who, from where, networked or not, is either accepted to login or not. Have a look at “/etc/login.access” to get an idea how that looks like. Here are some examples from that file:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Disallow console logins to all but a few accounts. # #-:ALL EXCEPT wheel shutdown sync:console # # Disallow non-local logins to privileged accounts (group wheel). # #-:wheel:ALL EXCEPT LOCAL .win.tue.nl # # Some accounts are not allowed to login from anywhere: # #-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL # # All other accounts are allowed to login from anywhere. |
That’s it for today. In the next post we’ll look at services are managed in FreeBSD.