this approach lets me persist the password. persisting /etc/shadow directly wasn't so feasible. populating /etc/shadow at activation time is something nix already does and is easy to plug into. so we store the passwd hash in this repo, but encrypt it to the destination machine's ssh pubkey to add enough entropy that it's not brute-forceable through the public git repo.
148 lines
5.3 KiB
Nix
148 lines
5.3 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
|
|
# installer docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/installation-device.nix
|
|
with lib;
|
|
let
|
|
cfg = config.sane.users;
|
|
# see nixpkgs/nixos/modules/services/networking/dhcpcd.nix
|
|
hasDHCP = config.networking.dhcpcd.enable &&
|
|
(config.networking.useDHCP || any (i: i.useDHCP == true) (attrValues config.networking.interfaces));
|
|
|
|
in
|
|
{
|
|
options = {
|
|
sane.users.guest.enable = mkOption {
|
|
default = false;
|
|
type = types.bool;
|
|
};
|
|
};
|
|
|
|
config = {
|
|
# Users are exactly these specified here;
|
|
# old ones will be deleted (from /etc/passwd, etc) upon upgrade.
|
|
users.mutableUsers = false;
|
|
|
|
# docs: https://nixpkgs-manual-sphinx-markedown-example.netlify.app/generated/options-db.xml.html#users-users
|
|
users.users.colin = {
|
|
# sets group to "users" (?)
|
|
isNormalUser = true;
|
|
home = "/home/colin";
|
|
uid = config.sane.allocations.colin-uid;
|
|
# i don't get exactly what this is, but nixos defaults to this non-deterministically
|
|
# in /var/lib/nixos/auto-subuid-map and i don't want that.
|
|
subUidRanges = [
|
|
{ startUid=100000; count=1; }
|
|
];
|
|
group = "users";
|
|
extraGroups = [
|
|
"wheel"
|
|
"nixbuild"
|
|
"networkmanager"
|
|
# phosh/mobile. XXX colin: unsure if necessary
|
|
"video"
|
|
"feedbackd"
|
|
"dialout" # required for modem access
|
|
];
|
|
|
|
# initial password is empty, in case anything goes wrong.
|
|
# if `colin-passwd` (a password hash) is successfully found/decrypted, that becomes the password at boot.
|
|
initialPassword = lib.mkDefault "";
|
|
passwordFile = lib.mkIf (config.sops.secrets ? "colin-passwd") config.sops.secrets.colin-passwd.path;
|
|
|
|
shell = pkgs.zsh;
|
|
# shell = pkgs.bashInteractive;
|
|
# XXX colin: create ssh key for THIS user by logging in and running:
|
|
# ssh-keygen -t ed25519
|
|
openssh.authorizedKeys.keys = [
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu colin@lappy"
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX colin@desko"
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX colin@servo"
|
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU colin@moby"
|
|
];
|
|
|
|
pamMount = {
|
|
# mount encrypted stuff at login
|
|
# requires that login password == fs encryption password
|
|
# fstype = "fuse";
|
|
# path = "${pkgs.gocryptfs}/bin/gocryptfs#/nix/persist/home/colin/private";
|
|
fstype = "fuse.gocryptfs";
|
|
path = "/nix/persist/home/colin/private";
|
|
mountpoint = "/home/colin/private";
|
|
options="nodev,nosuid,quiet,allow_other";
|
|
};
|
|
};
|
|
|
|
sane.impermanence.home-dirs = [
|
|
# cache is probably too big to fit on the tmpfs
|
|
# TODO: we could bind-mount it to something which gets cleared per boot, though.
|
|
".cache"
|
|
".cargo"
|
|
".rustup"
|
|
".ssh"
|
|
".local/share/keyrings"
|
|
];
|
|
|
|
sane.impermanence.service-dirs = mkIf cfg.guest.enable [
|
|
{ user = "guest"; group = "users"; directory = "/home/guest"; }
|
|
];
|
|
users.users.guest = mkIf cfg.guest.enable {
|
|
isNormalUser = true;
|
|
home = "/home/guest";
|
|
uid = config.sane.allocations.guest-uid;
|
|
subUidRanges = [
|
|
{ startUid=200000; count=1; }
|
|
];
|
|
group = "users";
|
|
initialPassword = lib.mkDefault "";
|
|
shell = pkgs.zsh;
|
|
openssh.authorizedKeys.keys = [
|
|
# TODO: insert pubkeys that should be allowed in
|
|
];
|
|
};
|
|
|
|
users.users.dhcpcd = mkIf hasDHCP {
|
|
uid = config.sane.allocations.dhcpcd-uid;
|
|
};
|
|
users.groups.dhcpcd = mkIf hasDHCP {
|
|
gid = config.sane.allocations.dhcpcd-gid;
|
|
};
|
|
|
|
security.sudo = {
|
|
enable = true;
|
|
wheelNeedsPassword = false;
|
|
};
|
|
|
|
services.openssh = {
|
|
enable = true;
|
|
permitRootLogin = "no";
|
|
passwordAuthentication = false;
|
|
};
|
|
|
|
# affix some UIDs which were historically auto-generated
|
|
users.users.sshd.uid = config.sane.allocations.sshd-uid;
|
|
users.groups.polkituser.gid = config.sane.allocations.polkituser-gid;
|
|
users.groups.sshd.gid = config.sane.allocations.sshd-gid;
|
|
users.groups.systemd-coredump.gid = config.sane.allocations.systemd-coredump-gid;
|
|
users.users.nscd.uid = config.sane.allocations.nscd-uid;
|
|
users.groups.nscd.gid = config.sane.allocations.nscd-gid;
|
|
users.users.systemd-oom.uid = config.sane.allocations.systemd-oom-uid;
|
|
users.groups.systemd-oom.gid = config.sane.allocations.systemd-oom-gid;
|
|
|
|
# guarantee determinism in uid/gid generation for users:
|
|
assertions = let
|
|
uidAssertions = builtins.attrValues (builtins.mapAttrs (name: user: {
|
|
assertion = user.uid != null;
|
|
message = "non-deterministic uid detected for: ${name}";
|
|
}) config.users.users);
|
|
gidAssertions = builtins.attrValues (builtins.mapAttrs (name: group: {
|
|
assertion = group.gid != null;
|
|
message = "non-deterministic gid detected for: ${name}";
|
|
}) config.users.groups);
|
|
autoSubAssertions = builtins.attrValues (builtins.mapAttrs (name: user: {
|
|
assertion = !user.autoSubUidGidRange;
|
|
message = "non-deterministic subUids/Guids detected for: ${name}";
|
|
}) config.users.users);
|
|
in uidAssertions ++ gidAssertions ++ autoSubAssertions;
|
|
};
|
|
}
|