2

I want to execute composer as non root user. So I renamed main composer executable file to composer-call and then create another executable file called composer.

#composer.sh
su nginx -c "composer-call ${@}" 

Now when I execute composer --version it works but while pass some more complex parameters like composer create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction it shows below error

+ su nginx -c 'composer-call create-project' drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction
su: unrecognized option: stability

Update-1

As per below answer by Jeff I tried his answer but it gives me another error. Expecting a better solution.

composer-call: line 2: can't open ?php: no such file
composer-call: line 3: /bin: Permission denied
+ su nginx -- composer-call composer-call docker-php-entrypoint docker-php-ext-configure docker-php-ext-enable docker-php-ext-install docker-php-source drupal drush pear peardev pecl phar phar.phar php php-config phpdbg phpize This file is part of Composer.
su: must be suid to work properly
+ su nginx -- composer-call composer-call docker-php-entrypoint docker-php-ext-configure docker-php-ext-enable docker-php-ext-install docker-php-source drupal drush pear peardev pecl phar phar.phar php php-config phpdbg phpize
su: must be suid to work properly
composer-call: line 6: syntax error: unexpected word (expecting ")")

NOTE: composer-call is a php cli script.

SkyRar
  • 181
  • 2
  • 12
  • To tackle the (second) problem first, `su: must be suid to work properly` -- it appears your `su` is not setuid. Can you repair that? – Jeff Schaller Oct 18 '18 at 12:47
  • The 3rd problem appears to be a typo of some sort in the `composer-call` script; perhaps you could share that portion of the script? – Jeff Schaller Oct 18 '18 at 12:47

3 Answers3

3

This is because "$@" is expanded into separate words and the second word (--stability in your case) is considered an option to su. Here is an example using ls as a command:

#composer.sh
set -x
su nginx -c "ls $@"

Output 1:

$ ./composer.sh -l -a
++ su nginx -c 'ls -l' -a
su: invalid option -- 'a'
Try 'su --help' for more information.

Note that only the first parameter (-l) goes into the string passed to -c. This is why your --version case works. Instead of $@ you should use $* which is expanded into a single word when it is placed in double quotes:

#composer.sh
set -x
su nginx -c "ls $*"

Output 2:

$ ./composer.sh -l -a
++ su nginx -c 'ls -l -a'
total 12
drwxr-xr-x 2 root root 4096 Apr 22 11:05 .
drwxr-xr-x 1 root root 4096 Apr 22 11:05 ..
-rwxr-xr-x 1 root root   28 Apr 22 11:05 composer.sh
-rw-r--r-- 1 root root    0 Apr 22 11:05 test.txt

Note that now all parameters go to -c which produces the correct result.

You can read about it in detail in man bash in the Special Parameters section.

user3071170
  • 131
  • 2
1

Use -- to separate the target user's command so that su doesn't try to parse additional arguments:

#composer.sh
su nginx -- composer-call "${@}" 

as an example:

user1's composer.sh:

#!/bin/sh
set -x
su otheruser -- /home/otheruser/composer-call "$@"

/home/otheruser/composer-call:

#!/bin/sh
echo Hi, I am composer-call, with arguments:
printf '%s\n' "$@"

In action:

./composer.sh create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction
+ su otheruser -- /home/otheruser/composer-call create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction
Password:
Hi, I am composer-call, with arguments:
create-project
drupal-composer/drupal-project:8.x-dev
some-dir
--stability
dev
--no-interaction
Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
  • This gives me below error composer-call: line 2: can't open ?php: no such file composer-call: line 3: /bin: Permission denied su: must be suid to work properly su: must be suid to work properly composer-call: line 6: syntax error: unexpected word (expecting ")") – SkyRar Oct 18 '18 at 00:50
  • It seems like it's invoking composer-call correctly, and you've maybe run into another hurdle? – Jeff Schaller Oct 18 '18 at 00:51
  • In my code composer was getting called correctly also. E.g as per my code composer -V was showing proper output. But when passing multiple parameters it shows error. – SkyRar Oct 18 '18 at 00:55
  • it was correctly calling compose-call and passing *some* arguments, but `su` was getting in the way when it saw arguments with leading dashes. Using a double-dash tells `su` that it should stop looking for options and pass everything else along to composer-call. – Jeff Schaller Oct 18 '18 at 01:17
  • Okay. Fine but this answer doesn't solve my issue. Is it working for you ? – SkyRar Oct 18 '18 at 01:19
  • I don't have your exact environment, so I mocked up a simulation. I'll demonstrate the results in an upcoming edit to answer. – Jeff Schaller Oct 18 '18 at 01:23
  • To install composer you just need to execute curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin -- --filename=composer-call – SkyRar Oct 18 '18 at 01:25
  • Does that really work? That's not how su is documented to take the command. – glenn jackman Oct 18 '18 at 12:42
  • That's how I got it to work; I couldn't quote the whole command nor prevent `su` from attempting to find options that didn't belong to it. – Jeff Schaller Oct 18 '18 at 12:48
0

Finally encapsulating the composer command inside a here-doc block helped me

#!/bin/sh

su nginx -s /bin/sh <<EOF
composer-call "$@"
EOF

I use below code to create above wrapper script programmatically. :)

printf '%s\n' "#!/bin/sh" "su nginx -s /bin/sh <<EOF" "composer-call \$@" "EOF" > /usr/local/bin/composer

Apart from it su-exec is also a nice alternative. It is a tinny software and can be installed like apk add su-exec

SkyRar
  • 181
  • 2
  • 12
  • 1
    That will only work as long as the arguments contain no spaces or special characters. Please see my answer [here](https://unix.stackexchange.com/a/479645/308316). –  Nov 04 '18 at 12:01
  • It works perfectly for arguments like `create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction ` – SkyRar Nov 04 '18 at 12:04
  • 1
    It may work with those particular arguments in your case, but your answer is public, and that use of `$@` is **flawed**. Try this instead: `su nginx -c 'composer-call "$@"' -- argv0 "$@"`. –  Nov 04 '18 at 12:14
  • Thanks @mosvy I will surely try your answer also. – SkyRar Nov 04 '18 at 12:21
  • Your new version will pass everything as a single argument to `composer-call` -- ie. it won't work at all (but you could "split" it by passing arguments of the form `"; reboot; "` ;-)). And it will also expand backquotes, variables, `$((...))`, etc. in the arguments. –  Nov 04 '18 at 13:21
  • @mosvy The parameters should not be spilted in my case and should be passed as a single argument to composer-call. Atleast for composer everything works perfectly as I tested it. So please don't guess that it will not work without testing it. And I don't understand why it is required to split the parameters ?? – SkyRar Nov 10 '18 at 10:28