Skip to content

Conversation

andrenth
Copy link
Contributor

@andrenth andrenth commented Feb 4, 2020

This patch allows FPM pool configuration settings listen.owner and listen.group to accept numeric IDs as well as user and group names. This makes them mirror the behavior of the user and group settings.

This feature was requested on bug #77062 in the bug tracker

@andrenth
Copy link
Contributor Author

So, there were CI failures but they seem unrelated to the PR.

This patch is useful for users with remote user databases (e.g. LDAP), where there's an inherent race between user creation and FPM pool creation. When the pool is created before the user has been created, FPM dies on reload due to the nonexistent user, taking down all pools. Accepting numeric IDs would prevent this from happening.

@nikic
Copy link
Member

nikic commented Feb 21, 2020

Looks fine to me, but let's let @bukka have a look as well.

@nikic nikic added the Feature label Feb 21, 2020
Copy link
Member

@bukka bukka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it makes sense in general.

It might be also possible to add some basic tests. It will be probably reliable only for the current user and some non existent user for errors. That should be enough to verify that uid and gid is accepted.

@@ -163,27 +163,35 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
#endif

if (c->listen_owner && *c->listen_owner) {
struct passwd *pwd;
if (strlen(c->listen_owner) == strspn(c->listen_owner, "0123456789")) {
wp->socket_uid = strtoul(c->listen_owner, 0, 10);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please verify that the uid exists using getpwuid and fail if it doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The motivation for this patch is to allow the code to work even when UID and GID don't correspond to a valid used (yet). The use case is for situations where creating the user might take longer than creating the pool when using remote authentication, like when using LDAP, and using the numeric IDs would allow FPM to successfully be reloaded when the pool is created, even if the user does not yet exist.

The patch mirrors the code for the user and group settings, which also do not fail if the IDs are not found.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok that makes sense.

}

if (c->listen_group && *c->listen_group) {
struct group *grp;
if (strlen(c->listen_group) == strspn(c->listen_group, "0123456789")) {
wp->socket_gid = strtoul(c->listen_group, 0, 10);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please verify that the gid exists using getgrgid and fail if it doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my comment w.r.t listen_owner above.

@bukka
Copy link
Member

bukka commented Mar 1, 2020

So the only missing bit is a test. Otherwise it looks good.

@andrenth
Copy link
Contributor Author

andrenth commented Mar 2, 2020

I've added a simple test file.

@andrenth
Copy link
Contributor Author

andrenth commented Mar 2, 2020

I believe the tests failed because they're not run as root. Is there a way to skip a test case unless it's running as root?

Edit: I've added a commit for that, thought I'm not sure it's the best way.

EOT;

$tester = new FPM\Tester($cfg);
$tester->start();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested this to run under root and it actually fails to start for me. Might be my local issue but was wondering if the test actually works for you when you run it under root?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw for me this line fails if run under root even with setting TEST_FPM_RUN_AS_ROOT=1:

$this->masterProcess = proc_open($cmd, $desc, $pipes);

Interestingly I tested starting that with passthru and it actually starts FPM so something weird with proc_open. Might need to debug it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does work for me:

$ sudo -i
# cd /home/andre/git/forks/php-src
# TEST_FPM_RUN_AS_ROOT=1 ./sapi/cli/php run-tests.php sapi/fpm/tests/socket-uds-numeric-user-and-group-id.phpt

=====================================================================
PHP         : /home/andre/git/forks/php-src/sapi/cli/php 
PHP_SAPI    : cli
PHP_VERSION : 8.0.0-dev
ZEND_VERSION: 4.0.0-dev
PHP_OS      : Linux - Linux andre 4.15.0-88-generic #88-Ubuntu SMP Tue Feb 11 20:11:34 UTC 2020 x86_64
INI actual  : /home/andre/git/forks/php-src
More .INIs  :   
---------------------------------------------------------------------
PHP         : /home/andre/git/forks/php-src/sapi/phpdbg/phpdbg 
PHP_SAPI    : phpdbg
PHP_VERSION : 8.0.0-dev
ZEND_VERSION: 4.0.0-dev
PHP_OS      : Linux - Linux andre 4.15.0-88-generic #88-Ubuntu SMP Tue Feb 11 20:11:34 UTC 2020 x86_64
INI actual  : /home/andre/git/forks/php-src
More .INIs  : 
---------------------------------------------------------------------
CWD         : /home/andre/git/forks/php-src
Extra dirs  : 
VALGRIND    : Not used
=====================================================================
Running selected tests.
PASS FPM: UNIX socket owner and group settings can be numeric [sapi/fpm/tests/socket-uds-numeric-user-and-group-id.phpt] 
=====================================================================
Number of tests :    1                 1
Tests skipped   :    0 (  0.0%) --------
Tests warned    :    0 (  0.0%) (  0.0%)
Tests failed    :    0 (  0.0%) (  0.0%)
Tests passed    :    1 (100.0%) (100.0%)
---------------------------------------------------------------------
Time taken      :    0 seconds
=====================================================================

The test calls the stat command to check the owner and group of the created sockets. Could it be a PATH issue, or maybe your environment doesn't have this command?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably something local then. Don't worry about it then!

*/
static public function skipIfNotRoot()
{
if (getmyuid() != 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem correct as it's just for the actual script owner but we should care about the EUID so it should be more something like this:

        if (
            (extension_loaded('posix_geteuid') && posix_geteuid() !== 0) ||
            (exec('id -u', $output, $rv) !== "0")
        ) {
            die('skip not running as root');
        }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't know that's what getmyuid did. However the test still works as intended because when the test is run as root, the PHP script generated from the phpt file will be owned by root, and so getmyuid will return 0.

I can add a commit to replace the test as you suggested, but in skipif.inc there's a similar check that also uses getmyuid:

// Running as root is not allowed without TEST_FPM_RUN_AS_ROOT env
if (!getmyuid() && !getenv('TEST_FPM_RUN_AS_ROOT')) {
    die('skip Refusing to run as root');
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah it should be enough then. I sometimes test things just by running php on the phpt file (without the framework) so that's why it didn't work for me but that's not requirement for the test to work so feel free to leave it as it is.

@bukka
Copy link
Member

bukka commented Mar 8, 2020

That root test looks like a good idea and it would be also good to have root tests for other things so well done on starting that! The only thing about those tests is that we won't be currently able to run them in CI. I think it would be idea to also have a test that we can run in CI. It could be basically the same as the current one, just it would use posix_geteuid return value in the config. It will at least test that numbers are accepted and should hopefully work. It will probably also require skip if posix extension is not loaded.

@andrenth
Copy link
Contributor Author

andrenth commented Mar 8, 2020

I've added a test that works as you suggested. I couldn't find a way to use PHP code or template variables in the EXPECT section of the test, so I went with EXPECTF.

@andrenth
Copy link
Contributor Author

andrenth commented Mar 9, 2020

So, the test does work locally, but it's failing in the CI environment:

ERROR: [pool unconfined] failed to chown() the socket '/home/vsts/work/1/s/sapi/fpm/tests/socket-uds-numeric-user-and-group-id-nonroot.9004.sock': Operation not permitted (1)

I don't know enough about the CI environment to understand why this happens. Do the tests run as a different user than the one who owns the test files?

@bukka
Copy link
Member

bukka commented Mar 11, 2020

it might be that Azure pipeline might be using seccomp or something maybe. Might make sense to skip just in there maybe as it works in Travis.

@nikic is it possible to skip Azure pipelines easily?

@nikic
Copy link
Member

nikic commented Mar 11, 2020

Looks like that did it. There's still a failure on macos though

fsockopen(): socket path exceeded the maximum allowed length of 104 bytes and was truncated in /Users/runner/runners/2.165.0/work/1/s/sapi/fpm/tests/fcgi.inc on line 280

@andrenth
Copy link
Contributor Author

I think renaming the test files to something shorter will fix that.

@bukka
Copy link
Member

bukka commented Mar 15, 2020

@andrenth It all looks good, just one small thing. Please can you update www.conf.in to somehow state that uid and gid are supported. Currently the description in there (

; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
) is a bit vague so it might be worth to extend it a little a bit or maybe just adding a note about what is accepted.

@andrenth
Copy link
Contributor Author

I've added a short note that those settings accept either names or numeric IDs. If you'd like me to extend the explanation a bit more, let me know.

@bukka
Copy link
Member

bukka commented Mar 15, 2020

Yeah I think it's good enough. Can you please squash all your commits and push force it?

@andrenth andrenth force-pushed the numeric-fpm-socket-owner-and-group branch from 64d0e8a to ac56ce1 Compare March 15, 2020 20:01
@andrenth
Copy link
Contributor Author

Done.

@bukka
Copy link
Member

bukka commented Mar 29, 2020

Merged to 7.4 as 0b4e80b

@bukka bukka closed this Mar 29, 2020
@andrenth
Copy link
Contributor Author

Thank you!

@hakuno
Copy link

hakuno commented Apr 16, 2020

Nice patch!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants