Skip to content

Commit f52ed4b

Browse files
committed
Merge pull request #16973 from wagnerluis1982
* gh-16973: Polish "Allow the user that runs the app to be specified via an env var" Allow the user that runs the app to be specified via an env var Closes gh-16973
2 parents ea6d9f3 + 79b5fd9 commit f52ed4b

File tree

7 files changed

+94
-3
lines changed

7 files changed

+94
-3
lines changed

spring-boot-project/spring-boot-docs/src/main/asciidoc/deployment.adoc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,9 +491,10 @@ For example, on Debian, you could use the following command:
491491
NOTE: The following is a set of guidelines on how to secure a Spring Boot application that runs as an init.d service.
492492
It is not intended to be an exhaustive list of everything that should be done to harden an application and the environment in which it runs.
493493

494-
When executed as root, as is the case when root is being used to start an init.d service, the default executable script runs the application as the user who owns the jar file.
495-
You should never run a Spring Boot application as `root`, so your application's jar file should never be owned by root.
496-
Instead, create a specific user to run your application and use `chown` to make it the owner of the jar file, as shown in the following example:
494+
When executed as root, as is the case when root is being used to start an init.d service, the default executable script runs the application as the user specified in the `RUN_AS_USER` environment variable.
495+
When the environment variable is not set, the user who owns the jar file is used instead.
496+
You should never run a Spring Boot application as `root`, so `RUN_AS_USER` should never be root and your application's jar file should never be owned by root.
497+
Instead, create a specific user to run your application and set the `RUN_AS_USER` environment variable or use `chown` to make it the owner of the jar file, as shown in the following example:
497498

498499
[indent=0,subs="verbatim,quotes,attributes"]
499500
----
@@ -708,6 +709,10 @@ The following environment properties are supported with the default script:
708709
The default depends on the way the jar was built but is usually `auto` (meaning it tries to guess if it is an init script by checking if it is a symlink in a directory called `init.d`).
709710
You can explicitly set it to `service` so that the `stop\|start\|status\|restart` commands work or to `run` if you want to run the script in the foreground.
710711

712+
| `RUN_AS_USER`
713+
| The user that will be used to run the application.
714+
When not set, the user that owns the jar file will be used.
715+
711716
| `USE_START_STOP_DAEMON`
712717
| Whether the `start-stop-daemon` command, when it's available, should be used to control the process.
713718
Defaults to `true`.

spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,21 @@ log_file="$LOG_FOLDER/$LOG_FILENAME"
128128
# shellcheck disable=SC2012
129129
[[ $(id -u) == "0" ]] && run_user=$(ls -ld "$jarfile" | awk '{print $3}')
130130

131+
# Run as user specified in RUN_AS_USER
132+
if [[ -n "$RUN_AS_USER" ]]; then
133+
if ! [[ "$action" =~ ^(status|run)$ ]]; then
134+
id -u "$RUN_AS_USER" || {
135+
echoRed "Cannot run as '$RUN_AS_USER': no such user"
136+
exit 2
137+
}
138+
[[ $(id -u) == 0 ]] || {
139+
echoRed "Cannot run as '$RUN_AS_USER': current user is not root"
140+
exit 4
141+
}
142+
fi
143+
run_user="$RUN_AS_USER"
144+
fi
145+
131146
# Issue a warning if the application will run as root
132147
[[ $(id -u ${run_user}) == "0" ]] && { echoYellow "Application is running as root (UID 0). This is considered insecure."; }
133148

spring-boot-tests/spring-boot-integration-tests/spring-boot-launch-script-tests/src/test/java/org/springframework/boot/launchscript/SysVinitLaunchScriptIT.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,38 @@ public void launchWithRelativeLogFolder(String os, String version) throws Except
267267
assertThat(output).contains("Log written");
268268
}
269269

270+
@ParameterizedTest(name = "{0} {1}")
271+
@MethodSource("parameters")
272+
public void launchWithRunAsUser(String os, String version) throws Exception {
273+
String output = doTest(os, version, "launch-with-run-as-user.sh");
274+
assertThat(output).contains("wagner root");
275+
}
276+
277+
@ParameterizedTest(name = "{0} {1}")
278+
@MethodSource("parameters")
279+
public void whenRunAsUserDoesNotExistLaunchFailsWithInvalidArgument(String os, String version) throws Exception {
280+
String output = doTest(os, version, "launch-with-run-as-invalid-user.sh");
281+
assertThat(output).contains("Status: 2");
282+
assertThat(output).has(coloredString(AnsiColor.RED, "Cannot run as 'johndoe': no such user"));
283+
}
284+
285+
@ParameterizedTest(name = "{0} {1}")
286+
@MethodSource("parameters")
287+
public void whenJarOwnerAndRunAsUserAreBothSpecifiedRunAsUserTakesPrecedence(String os, String version)
288+
throws Exception {
289+
String output = doTest(os, version, "launch-with-run-as-user-preferred-to-jar-owner.sh");
290+
assertThat(output).contains("wagner root");
291+
}
292+
293+
@ParameterizedTest(name = "{0} {1}")
294+
@MethodSource("parameters")
295+
public void whenLaunchedUsingNonRootUserWithRunAsUserSpecifiedLaunchFailsWithInsufficientPrivilege(String os,
296+
String version) throws Exception {
297+
String output = doTest(os, version, "launch-with-run-as-user-root-required.sh");
298+
assertThat(output).contains("Status: 4");
299+
assertThat(output).has(coloredString(AnsiColor.RED, "Cannot run as 'wagner': current user is not root"));
300+
}
301+
270302
static List<Object[]> parameters() {
271303
List<Object[]> parameters = new ArrayList<>();
272304
for (File os : new File("src/test/resources/conf").listFiles()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
source ./test-functions.sh
2+
install_service
3+
4+
echo 'RUN_AS_USER=johndoe' > /test-service/spring-boot-app.conf
5+
6+
start_service
7+
echo "Status: $?"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
source ./test-functions.sh
2+
install_service
3+
4+
useradd wagner
5+
echo 'RUN_AS_USER=wagner' > /test-service/spring-boot-app.conf
6+
7+
useradd phil
8+
chown phil /test-service/spring-boot-app.jar
9+
10+
start_service
11+
await_app
12+
13+
ls -la /var/log/spring-boot-app.log
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
source ./test-functions.sh
2+
install_service
3+
4+
useradd wagner
5+
echo 'RUN_AS_USER=wagner' > /test-service/spring-boot-app.conf
6+
echo "JAVA_HOME='$JAVA_HOME'" >> /test-service/spring-boot-app.conf
7+
8+
su - wagner -c "$(which service) spring-boot-app start"
9+
echo "Status: $?"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
source ./test-functions.sh
2+
install_service
3+
4+
useradd wagner
5+
echo 'RUN_AS_USER=wagner' > /test-service/spring-boot-app.conf
6+
7+
start_service
8+
await_app
9+
10+
ls -la /var/log/spring-boot-app.log

0 commit comments

Comments
 (0)