Pyinfra: Incorrect su usage for FreeBSD

Created on 23 Jul 2020  Â·  14Comments  Â·  Source: Fizzadar/pyinfra

Using su results in hanging processes running:
su root -s /bin/sh -c stat /etc/installurl 1> /dev/null 2> /dev/null && (stat -c 'user=%U…

This is most likely because -s does not mean something like "shell", it means "Set the MAC label to the user's default label as part of the user credential setup" (so does not take an argument), so we end up with sh launched:

ec2-user@freebsd:~ $ su root -s /bin/sh -c stat /etc/installurl
root@freebsd:/home/ec2-user #

While this works:

ec2-user@freebsd:~ $ su root -s -c stat /etc/installurl
1895890688 94 crw--w---- 1 ec2-user tty 94 0 "Jul 23 12:36:27 2020" "Jul 23 12:36:27 2020" "Jul 23 12:36:27 2020" "Dec 31 23:59:59 1969" 4096 0 0 /dev/pts/0
ec2-user@freebsd:~ $ su root -c stat /etc/installurl
1895890688 94 crw--w---- 1 ec2-user tty 94 0 "Jul 23 12:36:27 2020" "Jul 23 12:36:27 2020" "Jul 23 12:36:27 2020" "Dec 31 23:59:59 1969" 4096 0 0 /dev/pts/0
Bug

All 14 comments

@myfreeweb
I would like to see the example deploy that is causing this problem for you. I agree the issue is su is handled differently in FreeBSD. However, I still feel you are using it incorrectly. That's why I want to see what your deploy looks like.

How can I use some.thing(…, su_user='root') incorrectly? And how would anything in the usage code affect how the command is generated?

Sorry, I wasn't clear. I'm referring to your While this works section. I'm assuming this is you running the su string on the command line. If this assumption is incorrect, I'm sorry.
More detail on why I feel your While this works is incorrect (assuming this is you running on the cli)
-c stat seems like you are wanting to use the stat command on /etc/installurl. However, you can see in your output the file is not /etc/installurl, instead the file is /dev/pts/0.
I understand the issue with su specifically in this case. However I would still like to see your deploy code so I fully understand how to reproduce this.

Oh. Yes, the assumption is correct. Right, I read this as "you are using it [the deploy] incorrectly" lol.

However, you can see in your output the file is not /etc/installurl, instead the file is /dev/pts/0.

hmmmm.

Oh okay so I understand it now.

https://www.freebsd.org/cgi/man.cgi?su#end

The -c at the end is supposed to pass to the shell, but the shell expects one argument, so quoting is required.

su root -c 'stat /etc/installurl'

so I fully understand how to reproduce this

pkg.packages(packages=['git'], su_user='root') is the first command (and the one that fails), I thought this would fail with anything with su_user.

I think what is happening here is the su command requires a password. There is no way to pass a password to the su command. Therefore it prompts for the password. That's why the command is blocking. It's waiting for a password.

Instead of using su, you'll want to use sudo, which you can pass the password.

I confirmed this does work on a freebsd virualbox where the ssh user is osboxes.

pkg.packages(packages=['git'], sudo=True, sudo_user='root')

No, the EC2 AMIs do not have a root password! If I had to enter a password when trying the commands manually, I would've noticed that ;)

I already mentioned that the problem might be lack of quoting.

Instead of using su, you'll want to use sudo

sudo is not installed by default, so I'd need to su to install sudo.

Of course I could do that with an external ssh command before launching pyinfra, but that's not great :/

I understand your point on quoting. I looked into that and it looks fine. My testing leads me to believe the command is blocking for a password to the su command.
Try this for me. Obtain a cli as your non-root user on the freebsd system. Then run:
su root
What do you see? I believe you'll see a prompt for a password.
This is what I see.

$ su root
Password:
root@osboxes:/usr/home/osboxes

I think I understand this issue now - I believe this is the same as https://github.com/Fizzadar/pyinfra/issues/412. More specifically the issue is BSD su doesn't accept a -c parameter (or any command) and is thus not possible to use within pyinfra as it will only open a shell (which causes pyinfra to hang).

The solution is to use sudo which does allow specifying a command.

Leaving this open while I explore ways to better highlight and/or detect this, and notify the user.

It passes the -c and everything to the shell, which supports it. Please look at the first post here for an example.

My apologies I wasn't clear there; my point is that on BSD systems the -c argument has a different value to Linux, according to the manpages:

# Ubuntu 18 / Linux
       su [options] [username]
       ...
       -c, --command COMMAND
           Specify a command that will be invoked by the shell using its -c.

# FreeBSD 12 / BSD/MacOS
     su [-] [-c class] [-flms] [login [args]]
       ...
     -c class
             Use the settings of the specified login class.  The login class
             must be defined in login.conf(5).  Only allowed for the super-
             user

So the assumption that all *nix su/sudo implementations are the same is incorrect. The only way to handle this I can think of is do some OS detection via uname -s at connect time, and change arguments according to that.

Yes, the assumption is incorrect. It is a bit weird: the -c argument to su that's described above is not what you want, you want the -c to be passed to the shell via the [args]. (If it's after the login it won't be processed by su)

This should now be fixed by https://github.com/Fizzadar/pyinfra/commit/0c7234be0c3d02172da63f843260851b7ff66794! I've released this immediately as 1.1.dev2 and, assuming no further issues, will release 1.1 this week :)

This has now been released in v1.1 🎉!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Bystroushaak picture Bystroushaak  Â·  4Comments

makkus picture makkus  Â·  5Comments

pathcl picture pathcl  Â·  4Comments

Tezar picture Tezar  Â·  5Comments

behrmann picture behrmann  Â·  5Comments