Managing /etc/passwd, /etc/group and shadow files
Konfigkoll has special support for managing /etc/passwd
, /etc/group
and
/etc/shadow
. This is because these files contain contents from multiple
sources (various packages add their own users) and it is difficult to manage
these otherwise.
The interface to this is the ::passwd::Passwd
type
(API docs).
Typically, you would:
- Create an instance of
::passwd::Passwd
early in the main phase - Add things to it as needed (next to the associated packages)
- Apply it at the end of the main phase
A rough example (we will break it into chunks down below):
// Mappings for the IDs that systemd auto-assigns inconsistently from computer to computer
const USER_MAPPING = [("systemd-journald", 900), /* ... */]
const GROUP_MAPPING = [("systemd-journald", 900), /* ... */]
pub async fn phase_main(props, cmds, package_managers) {
let passwd = passwd::Passwd::new(USER_MAPPING, GROUP_MAPPING)?;
let files = package_managers.files();
// These two files MUST come first as other files later on refer to them,
// and we are not order independent (unlike the real sysusers.d).
passwd.add_from_sysusers(files, "systemd", "/usr/lib/sysusers.d/basic.conf")?;
passwd.add_from_sysusers(files, "filesystem", "/usr/lib/sysusers.d/arch.conf")?;
// Various other packages and other changes ...
passwd.add_from_sysusers(files, "dbus", "/usr/lib/sysusers.d/dbus.conf")?;
// ...
// Give root a login shell, we don't want the default /usr/bin/nologin!
passwd.update_user("root", |user| {
user.shell = "/bin/zsh";
user
});
// Add human user
let me = passwd::User::new(1000, "me", "me", "");
me.shell = "/bin/zsh";
me.home = "/home/me";
passwd.add_user_with_group(me);
passwd.add_user_to_groups("me", ["wheel", "optical", "uucp", "users"]);
// Don't store passwords in your git repo, load them from the system instead
passwd.passwd_from_system(["me", "root"]);
// Deal with the IDs not matching (because the mappings were created
// before konfigkoll was in use for example)
passwd.align_ids_with_system()?;
// Apply changes
passwd.apply(cmds)?;
}
USER_MAPPING
and GROUP_MAPPING
First up, there is special support for systemd's /usr/lib/sysusers.d/
files.
These often don't declare the specific user/group IDs, but instead auto-assign them.
This creates a bit of chaos between computers and there is no auto-assign logic
in Konfigkoll (yet?). To solve both of these issues we need to declare which
IDs we want for the auto-assigned IDs if we are to use sysusers.d
-integration.
That is what the USER_MAPPING
and GROUP_MAPPING
constants are for.
General workflow
The idea is (as stated above) to create one instance of Passwd
, update it
as you go along, and then write out the result at the end:
let passwd = passwd::Passwd::new(USER_MAPPING, GROUP_MAPPING)?;
// Do stuff
passwd.apply(cmds)?;
Now, what about the "stuff" you can "do"?
Adding a system user / group
The easiest option (when available) is passwd.add_from_sysusers
. Arch Linux
uses this for (almost?) all users created by packages. Debian however doesn't.
If there isn't a corresponding sysusers file to add you need to create the user yourself. This will be pretty much like the example of adding a human user below.
Patching a user or group
Sometimes you need to make changes to a user or group created by sysusers. This
can be done by passing a function to passwd.update_user
or passwd.update_group
.
// Give root a login shell, we don't want the default /usr/bin/nologin!
passwd.update_user("root", |user| {
user.shell = "/bin/zsh";
user
});
The |...| { code }
syntax is a closure, a way to declare an inline function
that you can pass to another function. The bits between the |
are the parameters
that the function takes.
Adding a human user
There isn't too much code needed for this (and remember, you could always create a utility function if you need this a lot):
// Add human user
let me = passwd::User::new(1000, "me", "me", "");
me.shell = "/bin/zsh";
me.home = "/home/me";
// Add them to the passwd database (and automatically create a corresponding group)
passwd.add_user_with_group(me);
// Add the user to some extra groups as well
passwd.add_user_to_groups("me", ["wheel", "optical", "uucp", "users"]);
Passwords
What about setting the password? Well, it isn't good practise to store those passwords in your git repository. Instead, you can read them from the system:
passwd.passwd_from_system(["me", "root"]);
This will make me
and root
have whatever password hashes they current already
have on the system.
IDs not matching
If you already have several computers before starting with konfigkoll, chances
are the user and group IDs don't match up. This can be fixed with passwd.align_ids_with_system
.
This will copy the IDs from the system so they match up.
Of course the assignment of IDs on now your computers won't match, but the users and groups will match whatever IDs are on the local file system.