I am trying to run some scripts as different users in Alpine Docker image. While logged in as devuser
I want run setup.sh
as root
and app.sh
as appuser
. Since I have read that you can't use the SUID on script files I have a couple C programs, setup
and app
, that call the scripts. I can run setup
as root
but can't run app
as appuser
.
Here is the content of the directories. Notice that app and setup have the SUID bit set on the programs. I tried setting the SUID on the scripts but that didn't work.
/opt/app $ ls -l
total 24552
-r-sr-xr-x 1 appuser appgroup 10632 Jun 27 12:59 app
-r-------- 1 appuser appgroup 25101769 Jun 27 12:59 app.jar
-r-xr-xr-- 1 appuser appgroup 327 Jun 27 12:59 app.sh
-r-------- 1 appuser appgroup 316 Jun 27 12:59 application.yml
-r-sr-xr-x 1 root root 10632 Jun 27 12:59 setup
-r-xr-xr-- 1 root root 152 Jun 27 12:59 setup.sh
I am running as devuser
.
/opt/app $ whoami
devuser
I can run setup
successfully. It is owned by root
and runs as root
.
/opt/app $ ./setup
Running As: root:appgroup
I try running app
and it does not change to the owner.
/opt/app $ ./app
Running As: devuser:appgroup
Error: Unable to access jarfile app.jar
Here is the source for setup.sh
.
/opt/app $ cat setup.sh
#!/bin/ash
echo "Running As: $(whoami):$(id -gn)"
Here is the source for app.sh
.
/opt/app $ cat app.sh
#!/bin/ash
echo "Running As: $(whoami):$(id -gn)"
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar $SPRING_OPTS
Here are the two users.
/opt/app $ id devuser
uid=701(devuser) gid=700(appgroup) groups=700(appgroup),700(appgroup)
/opt/app $ id appuser
uid=700(appuser) gid=700(appgroup) groups=700(appgroup),700(appgroup)
Here is the source for setup.c
.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
setuid(0);
system("./setup.sh");
return 0;
}
Here is the source for app.c
. I had tried setuid(0)
too but that did not work.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
setuid(700);
system("./app.sh");
return 0;
}
And finally my dirty laundry; the Docker build. I have tried setting up adduser
as a system user and a non-system user.
RUN addgroup -g 700 appgroup \
&& adduser -G appgroup -D -u 700 -S -H -s /bin/sh appuser \
&& adduser -G appgroup -D -u 701 -s /bin/sh devuser
RUN sh -c 'touch app.jar' \
&& chown appuser:appgroup *.jar \
&& chown appuser:appgroup *.yml \
&& chown appuser:appgroup app.sh \
&& chown appuser:appgroup app \
# && chmod -R 750 . \
&& chmod u+x,g+x,o-x *.sh \
&& chmod 4555 setup \
&& chmod 4555 app \
&& chmod 400 *.yml \
&& chmod 400 *.jar \
&& chmod -R -w .
USER devuser
# USER appuser
Thanks for any help, Wes.
If you change setuid()
to setreuid()
in app.c
it will work.
See this answer and comments for reason: https://stackoverflow.com/a/20687988
However, the way you describe is non-standard. It sounds hacky to have SUID binaries that call shell scripts.
Here's a way to do it using the Alpine standard su-exec:
app.sh
#!/bin/sh
echo "Running As: $(whoami):$(id -gn)"
java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar app.jar $SPRING_OPTS
setup.sh
#!/bin/sh
echo "Running As: $(whoami):$(id -gn)"
exec su-exec appuser:appgroup ./app.sh
Dockerfile
FROM alpine
WORKDIR /app
RUN apk add --no-cache su-exec
RUN addgroup -g 700 appgroup \
&& adduser -G appgroup -D -u 700 -S -H -s /bin/sh appuser
COPY * ./
RUN chmod 0755 *.sh
ENTRYPOINT ["./setup.sh"]
Much simpler, and probably much safer as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With