Bug 277210 - jail(8): exec.clean retrieves PWD from user info (can cause services to crash on jail start-up)
Summary: jail(8): exec.clean retrieves PWD from user info (can cause services to crash...
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: Unspecified
Hardware: Any Any
: --- Affects Many People
Assignee: freebsd-jail (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-02-21 11:55 UTC by johannes.kunde
Modified: 2024-03-14 17:35 UTC (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description johannes.kunde 2024-02-21 11:55:25 UTC
I recently ran into a problem where one of my services, configured via rc(8), 
failed to start, but only if the corresponding jail was (re)started as a whole.
The same service (re)started without problems when running 'service foo start'.
Digging into the problem, I found out that in case 'exec.clean' has been set in 
jail.conf(5), PWD is set to '/root' (only root:wheel can read here) when (re)starting the jail, 
but is set to '/' when running 'service foo start' from within the running jail. 
I think this behavior is caused by the following lines:
https://github.com/freebsd/freebsd-src/blob/main/usr.sbin/jail/command.c#L727-L797.

It is also not documented in the manpage that a chdir(2) happens with 'exec.clean'.

So far, I've found two ways to overcome this behavior. One is to omit 'exec.clean' in 
jail.conf(5), the other is to set '${name}_chdir="/"' in the corresponding rc(8) script.

However, I wanted to file this bug and discuss the problem, as I'm uncertain if this behavior 
is intended. When running the very same service on a FreeBSD host system (not in a jail) 
it starts up normally, because PWD will be "/" in any case. It's also quite hard to trace 
that issue down to the above lines, as there is no obvious error message other than what 
is printed out by the affected service itself.

The problem can be easily reproduced with the below C program and rc(8) script:

Compile the program with 'cc -o getcwd getcwd.c' and copy it to '/usr/local/sbin'.



#include <unistd.h>
#include <limits.h>
#include <syslog.h>

int main() {
	char cwd[PATH_MAX];
		if (getcwd(cwd, sizeof(cwd)) != NULL) {
			syslog(LOG_DEBUG, "Current working dir: %s\n", cwd);
		} else {
			syslog(LOG_DEBUG, "getcwd() error");
			return 1;
		}
	return 0;
}



Copy the following script to '/usr/local/etc/rc.d/getcwd' and run 'chmod +x /usr/local/etc/rc.d/getcwd'.



#!/bin/sh
#
# PROVIDE: getcwd

. /etc/rc.subr

name="getcwd"
rcvar="getcwd_enable"
command="/usr/local/sbin/${name}"

load_rc_config $name
: ${getcwd_enable:=NO}

run_rc_command "$1"



Run 'sysrc getcwd_enable="YES"'

Now do the following:
1. Restart the jail
2. Run 'service getcwd start'
3. Take a look at '/var/log/debug.log'


NOTE: This behavior is not bound to or related to any specific Jail-Manager.
Comment 1 Michael Osipov freebsd_committer freebsd_triage 2024-02-21 12:04:42 UTC
Please paste the output of syslog.
Comment 2 johannes.kunde 2024-02-21 12:22:05 UTC
That's how a 'service getcwd start' looks like:
Feb 21 10:55:07 testjail getcwd[4421]: Current working dir: /
That's how a (re)start of the jail looks like:
Feb 21 10:55:25 testjail getcwd[5256]: Current working dir: /root
Comment 3 Michael Osipov freebsd_committer freebsd_triage 2024-02-21 12:54:17 UTC
(In reply to johannes.kunde from comment #2)

The second case is problematic because if you do a su(1) or daemon(8) it will fail and depending on the code the executable might exit.
Comment 4 johannes.kunde 2024-02-21 13:05:13 UTC
(In reply to Michael Osipov from comment #3)

Correct, and that's exactly where I'm coming from. I've a GO webservice controlled by daemon(8) with '-u www' flag set and on jail start-up the program exits, because it trys to retrieve an absolute path at /root.
Comment 5 Jamie Gritton freebsd_committer freebsd_triage 2024-02-22 02:12:17 UTC
While it does make sense to root from the jail's root under exec.clean, it unfortunately clashes with a decade of current practice.

I'll consider a change, and see what people think, but I can't guarantee it'll happen given the long history.  It seems reasonable that if a user is specified (even directly like -U root), then it would still chdir to its home, but in the absence of a user, starting in "/" would in fact be the most expected outcome.
Comment 6 johannes.kunde 2024-02-22 07:09:44 UTC
(In reply to Jamie Gritton from comment #5)

> While it does make sense to root from the jail's root under exec.clean, it unfortunately clashes with a decade of current practice.

I get the idea. I believe it runs contrary to what most people expect to be the outcome, which would be a behavior consistent with the host system. A service running inside a jail with a user other than root most likely doesn't want to start at /root.

> It seems reasonable that if a user is specified (even directly like -U root), then it would still chdir to its home, but in the absence of a user, starting in "/" would in fact be the most expected outcome.

That sounds reasonable.

I think one of the main problems is also the lack of information when the problem occurs unexpectedly in the wild. All I got printed out from an external library, which caused the issue in my case, was this line:

> Feb 12 20:20:39 myjail myservice[9364]: 2024/02/12 20:20:39 stat .: permission denied

Starting from there, it's almost impossible to have a clue what's going on.
I don't know if there is any good chance to generate helpful output at runtime, but some hints in the manpage would help as well.
Comment 7 Michael Osipov freebsd_committer freebsd_triage 2024-02-22 08:06:45 UTC
From jail(8):


       exec.clean
	       Run commands in a clean environment.  The environment  is  dis-
	       carded  except  for HOME, SHELL,	TERM and USER.	HOME and SHELL
	       are set to the target login's default values.  USER is  set  to
	       the  target  login.  TERM is imported from the current environ-
	       ment.  The environment variables	from the login class  capabil-
	       ity database for	the target login are also set.

I completely miss the chdir(2) here with PWD. That is the first one to be fixed even if it gets reverted because it is not documented.
Comment 8 Frank Behrens 2024-03-14 16:39:49 UTC
I discovered the same problem, even without an exec.clean.

In  the script, called from the service start script is a
d=`pwd`
..
cd $d

which fails, after the effective user for the service has been changed.
Comment 9 johannes.kunde 2024-03-14 17:35:04 UTC
(In reply to Frank Behrens from comment #8)

> In  the script, called from the service
> start script is a
> d=`pwd`
> ..
> cd $d

Can you please provide the source? Would be great to have it tracked here.