error using symlink for script with php
I am using Unit with PHP, and running into an issue where if the script is using a symlink, I get an error "script is not under php root".
My unit configuration is:
{
"applications": {
"exphp74": {
"type": "php 7.4",
"user": "nobody",
"processes": 2,
"root": "/var/www/html",
"script": "app/start.php"
}
},
"listeners": {
"*:8374": {
"application": "exphp74"
}
}
}
/var/www/html/app is a symlink to /tmp/app. When I try to apply the above configuration, I get "Failed to apply new configuration." and unit.log has "script is not under php root".
My guess is because realpath is returning the actual path to the file even though it's also under the root through a symlink.
Is there a way to have Unit still consider it as under the php root even though it's a symlink?
I did notice that if I create /var/www/html/app as a real directory with start.php in it, apply the configuration, and then switch it back to a symlink, then all works correctly.
Thanks for your help.
Hello, Currently this check is hard-coded and cannot be avoided. Unit expects all php files inside application root. I admit the check is performed only during application startup this why you are able to change the file later.
If this is a development environment and you don't care about security, you may specify proxy php file as a target and include there any symlink you need.
Yes, I set my script to be a PHP file not in the symlink, and in that file, I can require the file I need in PHP itself. Thanks for your help.
@mar0x do you think it is possible (technically) to change this behaviour? Was playing around with Typo3 (CMS) on Unit and there docs are telling me to use symlinks for the typo_src. Will look into the PHP Module to learn more about the check but if you could point me in the right direction, that would be great.
So, there are two checks here in src/nxt_php_sapi.c::nxt_php_set_target() (and this only applies to the 'script' option and not 'index')
- Does the full path to the script exist?
- Is the script located under the 'root'?
Seeing as these checks are only done at start up and I would say are not really security related (if you want to confine the app we have better mechanisms in 'rootfs' etc), I think we could probably just drop the 2nd check...
It's common to have a router script outside of the public web root. In fact, a lot of people recommend storing the PHP source files outside of the public web root, for security reasons.
This is a typical PHP setup:
/app
/src
/class
/ ...
/router.php
/public_html
/logo.png
/whatever.txt
/vendor
In Nginx, I'd use the following server block to set up PHP:
{
server_name example.com;
root /app/public_html;
location / {
try_files $uri @php;
}
location @php {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /app/src/router.php;
}
}
However I don't think there's something compatible with this behaviour in Nginx Unit, so I am having to create an arbitrary php file that is solely used to require the actual router file. I understand that there's different routes that could possibly handle this, but my applications are built in a way that expect the public web root to be a sibling of the vendor directory.
@rbro You could try this patch
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 68ef07eb..373b0fb4 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -583,13 +583,6 @@ nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target,
target->script_filename.length = nxt_strlen(p);
target->script_filename.start = p;
- if (!nxt_str_start(&target->script_filename,
- target->root.start, target->root.length))
- {
- nxt_alert(task, "script is not under php root");
- return NXT_ERROR;
- }
-
ret = nxt_php_dirname(&target->script_filename,
&target->script_dirname);
if (nxt_slow_path(ret != NXT_OK)) {
@g105b To support a script with an absolute path would take a little re-factoring of code...
I wish I could help but my C skills are very dusty. The reason I'm reporting this is because I thought it was important to note that my applications that currently run in Nginx are not compatible with Nginx Unit, due to the decision of having no executable scripts within the web root.
I will take a look in due course. I don't see any reason not to allow it.
@g105b You could try giving this somewhat simple patch a spin. It simply lets you specify the "script" as an absolute path.
diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c
index 42fdbc68..260350b0 100644
--- a/src/nxt_php_sapi.c
+++ b/src/nxt_php_sapi.c
@@ -97,7 +97,6 @@ static void nxt_php_disable(nxt_task_t *task, const char *type,
static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir);
static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
-static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
nxt_inline u_char *nxt_realpath(const void *c);
static void nxt_php_request_handler(nxt_unit_request_info_t *req);
@@ -557,7 +556,10 @@ nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target,
if (value != NULL) {
nxt_conf_get_string(value, &str);
- nxt_php_str_trim_lead(&str, '/');
+ if (str.start[0] == '/') {
+ target->script_filename = str;
+ return NXT_OK;
+ }
tmp = nxt_malloc(target->root.length + 1 + str.length + 1);
if (nxt_slow_path(tmp == NULL)) {
@@ -903,16 +905,6 @@ nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
}
-static void
-nxt_php_str_trim_lead(nxt_str_t *str, u_char t)
-{
- while (str->length > 0 && str->start[0] == t) {
- str->length--;
- str->start++;
- }
-}
-
-
nxt_inline u_char *
nxt_realpath(const void *c)
{
Happy to hear any feedback, good or bad...
Will give it a go next week, thanks! :)