ryantm / agenix

age-encrypted secrets for NixOS and Home manager
https://matrix.to/#/#agenix:nixos.org
Creative Commons Zero v1.0 Universal
1.33k stars 106 forks source link

Agenix seems to expect that TMPDIR and the destination after encryption is on the same filesystem with mv -f in certain cases #257

Open mitchty opened 2 months ago

mitchty commented 2 months ago

With this layout:

$ df / /home /tmp      
Filesystem       1K-blocks      Used  Available Use% Mounted on
zroot/os/root   1058116992    596608 1057520384   1% /
zroot/user/home 1715647104 658126720 1057520384  39% /home
tmpfs             24936036     28688   24907348   1% /tmp

I get this when trying to edit an existing secret:

++ dirname git/gh-cli-pub.age
+ mkdir -p git
+ mv -f /tmp/tmp.CsA6RGWVJS/gh-cli-pub.age git/gh-cli-pub.age
mv: inter-device move failed: '/tmp/tmp.CsA6RGWVJS/gh-cli-pub.age' to 'git/gh-cli-pub.age'; unable to remove target
: Permission denied
+ cleanup
+ '[' -n x ']'
+ rm -rf /tmp/tmp.4ebOPFI9Ow
+ '[' -n x ']'
+ rm -rf /tmp/tmp.CsA6RGWVJS
zsh: exit 1     ( cd secrets; env EDITOR=vim nix run github:ryantm/agenix -- --verbose --edit)

This looks to be this mv: https://github.com/ryantm/agenix/blob/24a7ea390564ccd5b39b7884f597cfc8d7f6f44e/pkgs/agenix.sh#L188

I think using mv is slightly invalid in this use case, if we're nuking the /tmp/tmp. dir in exit/etc traps anyway in the cleanup function an install command to copy the reencrypted file or plain old copy would be less problematic as then rename() won't fail with EPERM underneath of mv if the underlying filesystem doesn't support rename() in this way.

Worthy of a pr to swap mv to copy or install instead here?

Ref from rename() on linux (note this is nixos 23.11 if it matters but its more glibc than anything):


       EPERM or EACCES
              The directory containing oldpath has the sticky bit
              (S_ISVTX) set and the process's effective user ID is
              neither the user ID of the file to be deleted nor that of
              the directory containing it, and the process is not
              privileged (Linux: does not have the CAP_FOWNER
              capability); or newpath is an existing file and the
              directory containing it has the sticky bit set and the
              process's effective user ID is neither the user ID of the
              file to be replaced nor that of the directory containing
              it, and the process is not privileged (Linux: does not
              have the CAP_FOWNER capability); or the filesystem
              containing oldpath does not support renaming of the type
              requested.

In this case we're hitting EPERM in mv via rename() and that last condition in that rename on this tmpfs fs is saying no in spite of the dir in /tmp and file being owned by my user.

mitchty commented 2 months ago

Looks like install hits similar issues but cp worked a treat: https://github.com/ryantm/agenix/commit/1ddd836893066c471e386a1fd47b691ec50f735e