How to set permissions to binary in a package? `crowdsec` without DynamiUser?

For this PR: https://github.com/NixOS/nixpkgs/pull/446307

  1. DynamicUser is giving me a hard time and I don’t see how you can solve those issues in a nice way so I’m thinking about to not use DynamicUser for the crowdsec module. Is that fine?
  2. With DynamicUser, every user who wanted to use cscli to “interact” with crowdsec, needed to provide their user password and are checked, if they even have permission to execute systemd-run). Without DynamicUser only a Error: while setting up trace directory: mkdir /var/lib/crowdsec: file exists is currently “checking” for the correct users to execute cscli which is obviously not… good. How do I allow only users which are in the crowdsec group to be able to execute cscli (but even then with a password or only with sudo)? Somehow I need to set the group of cscli and then the permissions. How do you do that for a nixos-module?
1 Like

I am not sure if I can follow completely, though have you looked into security.wrappers?

I don’t think security.wrappers is the answer here, cscli shouldn’t have a setuid bit.

The way e.g. nextcloud solves this is using sudo. You could try to get the UID of the user executing crowdsec at runtime and make the script call sudo with that UID.

Your systemd-exec approach seemed better, though, I think you just hadn’t figured out how to fix the completion files.

Your systemd-exec approach seemed better, though, I think you just hadn’t figured out how to fix the completion files.

Yes and also another problem (I need to have the crowdsec binary in PATH of the cscli wrapper)

Yes, it’s perfectly fine. DynamicUser is intended for services that don’t have many files or shared resources (sockets, etc.).

If you’re interested in other sandboxing features, you can turn them on even with static uid/gid.

That seems easy enough to fix, makeWrapper supports adding things to $PATH: Nixpkgs Reference Manual

Not to say you have to use DynamicUser, but when I wrote this module it was pretty easy to set DynamicUser and make a wrapper like the nextcloud one to still have cscli work. It’s a shame the crowdsec module was merged without looking much into my fork.

I’m a little busy currently, but I may have time over easter to sit down for some pair programming if that would help?

Oh good to know. I’ll try to find your fork.

If I don’t get it up and running until then, yes please. I wouldn’t mind :slight_smile:

but shouldn’t you use that in the package-declaration or can I also use it just in the module?

I’m currently having this:

  postInstall = ''
    wrapProgram $out/bin/cscli --prefix PATH:$out/bin/crowdsec
    # ..
  '';

but I’m still getting

Error: cscli explain: fail to run crowdsec for test: exec: "crowdsec": executable file not found in $PATH

Doing

  postInstall = ''
    makeWrapper $out/bin/cscli $wrapperfile --prefix PATH:$out/bin/crowdsec
    # ...
  '';

results into

       > Running phase: installPhase
       > dirname: unrecognized option '--prefix'

during the package build.

My guess is that you end up doing something in the module that ends up overwriting that. Maybe it’s double-wrapped, or you substitute in a version from somewhere else.

Haven’t looked at the PR in detail, so at best this is a guess, I’ll try to set aside some time to do that.

1 Like

After some further inspection, I have:

[root@crowdsec:~]# cat $(which cscli)
#!/nix/store/2hjsch59amjs3nbgh7ahcfzm2bfwl8zi-bash-5.3p9/bin/bash
exec systemd-run \
  --quiet \
  --pty \
  --wait \
  --collect \
  --pipe \
  --service-type=exec \
  --working-directory=/var/lib/crowdsec/data/hub \
  --property=ExecPaths="/etc/crowdsec/plugins" \
  --property=User=crowdsec \
  --property=Group=crowdsec \
  --property=DynamicUser=true \
  --property=StateDirectory="crowdsec" \
  --property=StateDirectoryMode="0750" \
  --property=ConfigurationDirectory="crowdsec" \
  --property=ConfigurationDirectoryMode="0750" \
  -- \
  /nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/cscli "$@"


[root@crowdsec:~]# cat /nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/cscli
#! /nix/store/2hjsch59amjs3nbgh7ahcfzm2bfwl8zi-bash-5.3p9/bin/bash -e
PATH=${PATH:+':'$PATH':'}
PATH=${PATH/':''/nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/crowdsec'':'/':'}
PATH='/nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/crowdsec'$PATH
PATH=${PATH#':'}
PATH=${PATH%':'}
export PATH
exec -a "$0" "/nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/.cscli-wrapped"  "$@"

[root@crowdsec:~]#

After trying out what the PATH= lines do in an extra script:

PATH=${PATH:+':'$PATH':'}
PATH=${PATH/':''/nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/crowdsec'':'/':'}
PATH='/nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/crowdsec'$PATH
PATH=${PATH#':'}
PATH=${PATH%':'}
echo $PATH

with bash /tmp/test.bash gives me:

❯ bash /tmp/test.bash
/nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/crowdsec:/home/tornax/.local/bin:/home/tornax/.cargo/bin:/run/wrappers/bin:/home/tornax/.nix-profile/bin:/nix/profile/bin:/home/tornax/.local/state/nix/profile/bin:/etc/profiles/per-user/tornax/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:~/.local/bin

which looks correct. The crowdsec binary is in PATH, because I can even execute the path:

[root@crowdsec:~]> /nix/store/zarv7ry73q6ccmin59bdflyf9463rn8z-crowdsec-1.7.6/bin/crowdsec --help
Usage of crowdsec:
  -c string
        configuration file (default "/etc/crowdsec/config.yaml")
  -cpu-profile string
        write cpu profile to file
  ...

Oh my god. I finally fixed it.

I learned two (important) things:

  1. The spaces next to the colon are REQUIRED to make wrapProgram work. So this is wrong:
wrapProgram $out/bin/cscli --prefix PATH:$out/bin/

but this is right:

  wrapProgram $out/bin/cscli --prefix PATH : $out/bin/
  1. Every path in PATH must point to a directory. So this is wrong:
  wrapProgram $out/bin/cscli --prefix PATH : $out/bin/crowdsec

but this is right

  wrapProgram $out/bin/cscli --prefix PATH : $out/bin/

So the final fix to add the following to the package:

postInstall = ''
    wrapProgram $out/bin/cscli --prefix PATH : $out/bin/
    #...
2 Likes

Hahaha, yep, that’d explain that. Good spot!