Journal quotidien de Laravel créé avec de fausses permissions

J’ai un script que j’utilise avec php artisan (avec l’ utilisateur root ), et parfois le fichier journal quotidien est créé avant que l’utilisateur d’Apache www-data ne le fasse – ce qui signifie que lorsqu’un utilisateur réel utilise mon application Web, l’erreur d’autorisation de dossier:

Impossible d’ouvrir le stream: Autorisation refusée

Je modifie les permissions sur www-data à chaque fois, mais je veux résoudre ce problème en faisant en sorte que le fichier journal soit toujours créé avec les permissions appropriées.

J’ai envisagé de créer un travail cron qui crée le fichier ou le touche pour s’assurer qu’il dispose des droits requirejs tous les jours, mais je recherche une meilleure solution qui ne repose pas sur un autre script.

Nous avons également envisagé d’envelopper php artisan dans un autre script pour nous assurer qu’il est toujours exécuté avec les informations d’ identification de www-data , mais certaines choses que nous voulons faire sont en réalité des procédures racine que Apache ne devrait pas pouvoir faire.

Plus de suggestions?

Commençons par ce qui est constant.

Vous avez une commande php artisan , exécutée par root .

Il est prudent de supposer que cette commande est exécutée quotidiennement.

Solution n ° 1:

Étant donné que l’utilisateur qui crée les fichiers est celui qui est autorisé à y écrire par défaut, nous pouvons séparer les journaux par utilisateur en tant que tels:

App/start/global.php

 /* |-------------------------------------------------------------------------- | Application Error Logger |-------------------------------------------------------------------------- | | Here we will configure the error logger setup for the application which | is built on top of the wonderful Monolog library. By default we will | build a basic log file setup which creates a single file for logs. | */ Log::useDailyFiles(storage_path().'/logs/laravel-'.get_current_user().'.log'); 

Si votre utilisateur www-data devait créer un journal des erreurs, il en résulterait: storage/logs/laravel-www-data-2015-4-27.log .

Si votre utilisateur root devait créer un journal des erreurs, cela se traduirait par: storage/logs/laravel-root-2015-4-27.log .

Solution n ° 2:

Changez le journal utilisé par votre commande artisan, dans votre script php.

Dans votre run() , ajoutez cette ligne au début:

 Log::useFiles(storage_path().'/logs/laravel-'.__CLASS__.'-'.Carbon::now()->format('Ym-d').'.log'); 

Si le nom de votre classe est ArtisanRunner , votre fichier journal sera:

storage/logs/laravel-ArtisanRunner-2015-4-27.log .

Conclusion: La solution numéro 1 est meilleure, car elle délimite vos journaux par utilisateur et aucune erreur ne se produit.

EDIT: Comme indiqué par jason, get_current_user() renvoie le nom du propriétaire du script. Par conséquent, pour que la solution n ° 1 s’applique, chown vos fichiers de classe artisan au nom d’utilisateur requirejs.

Pour Laravel 5.1, j’utilise les éléments suivants en bas de bootstrap/app.php (comme mentionné dans les documents ):

 /** * Configure Monolog. */ $app->configureMonologUsing(function(Monolog\Logger $monolog) { $filename = storage_path('logs/laravel-'.php_sapi_name().'.log'); $handler = new Monolog\Handler\RotatingFileHandler($filename); $monolog->pushHandler($handler); }); 

Il y a beaucoup d’autres gestionnaires que vous pouvez utiliser à la place, bien sûr.

Pour ce faire, vous devez utiliser la liste de contrôle d’access avancée sur vos fichiers et répertoires. setfacl serait votre réponse ici. Si vous voulez donner à l’ utilisateur de www-data les droits d’écrire sur les fichiers de root dans un répertoire spécifique, vous pouvez le faire comme ceci:

 setfacl -d -m default:www-data:you-chosen-group:rwx /my/folder 

Après avoir émis ceci, vous définissez des permissions sur rwx pour l’ rwx de www-data sur tous les fichiers de /my/folder/ peu importe qui les a créés. S’il vous plaît, voyez ceci et cette question pour référence. En outre, vous pouvez vérifier docs pour setfacl .

Faites-moi savoir si cela aide.

Pour moi, ce problème était beaucoup plus que les permissions de journalisation … J’avais des problèmes avec tout ce qui concernait les dossiers bootstrap / cache et de stockage où un utilisateur créerait un fichier / dossier et l’autre serait incapable de modifier / supprimer en raison de la norme 644 et 755 permissions.

Les scénarios typiques sont:

  • Le fichier bootstrap / cache / comstackd.php créé par l’utilisateur apache mais ne pouvant pas être modifié par l’utilisateur du compositeur lors de l’exécution de la commande d’installation du composeur

  • L’utilisateur apache créant un cache qui ne peut pas être effacé à l’aide de l’utilisateur compositeur

  • Les conditions de course de journal redoutées décrites ci-dessus.

Le rêve est que, quel que soit l’utilisateur qui crée le fichier / dossier, les autres utilisateurs devant accéder doivent avoir exactement les mêmes permissions que l’auteur original.

TL, DR?

Voici comment c’est fait.

Nous devons créer un groupe d’utilisateurs partagé appelé laravel, le groupe étant constitué de tous les utilisateurs qui ont besoin d’accéder aux répertoires de stockage et d’amorçage / cache. Ensuite, nous devons nous assurer que les fichiers et les dossiers nouvellement créés ont le groupe laravel et les permissions 664 et 775 respectivement.

Il est facile de le faire pour les fichiers / répertoires existants, mais un peu de magie est nécessaire pour modifier le fichier / dossier par défaut en créant des règles …

 ## create user group sudo groupadd laravel ## add composer user to group sudo gpasswd -a composer-user laravel ## add web server to group sudo gpasswd -a apache laravel ## jump to laravel path sudo cd /path/to/your/beautiful/laravel-application ## optional: if you've been playing around with permissions ## consider resetting all files and directories to the default sudo find ./ -type d -exec chmod 755 {} \; sudo find ./ -type f -exec chmod 644 {} \; ## give users part of the laravel group the standard RW and RWX ## permissions for the existing files and folders respectively sudo chown -R :laravel ./storage sudo chown -R :laravel ./bootstrap/cache sudo find ./storage -type d -exec chmod 775 {} \; sudo find ./bootstrap/cache -type d -exec chmod 775 {} \; sudo find ./storage -type f -exec chmod 664 {} \; sudo find ./bootstrap/cache -type f -exec chmod 664 {} \; ## give the newly created files/directories the group of the parent directory ## eg the laravel group sudo find ./bootstrap/cache -type d -exec chmod g+s {} \; sudo find ./storage -type d -exec chmod g+s {} \; ## let newly created files/directories inherit the default owner ## permissions up to maximum permission of rwx eg new files get 664, ## folders get 775 sudo setfacl -R -d -mg::rwx ./storage sudo setfacl -R -d -mg::rwx ./bootstrap/cache ## Reboot so group file permissions refresh (required on Debian and Centos) sudo shutdown now -r 

À des fins purement de débogage, j’ai trouvé utile de séparer les journaux en deux utilisateurs cli / web +. J’ai donc modifié légèrement la réponse de Sam Wilson. Mon cas d’utilisation était que la queue était exécutée sous son propre utilisateur, ce qui m’aidait à faire la distinction entre l’utilisateur du compositeur utilisant le cli (par exemple, les tests unitaires) et le démon de queue.

 $app->configureMonologUsing(function(MonologLogger $monolog) { $processUser = posix_getpwuid(posix_geteuid()); $processName= $processUser['name']; $filename = storage_path('logs/laravel-'.php_sapi_name().'-'.$processName.'.log'); $handler = new MonologHandlerRotatingFileHandler($filename); $monolog->pushHandler($handler); }); 

Laravel 5.1

Dans notre cas, nous voulions créer des fichiers journaux pour que tous les processus et utilisateurs du groupe de deploy aient des permissions en lecture / écriture. Nous avions donc besoin de nouveaux fichiers avec la permission 0664. Par défaut, les nouveaux fichiers journaux sont 0644.

Nous avons également ajouté un formateur pour append de nouvelles lignes et un journal plus lisible

 $app->configureMonologUsing(function(Monolog\Logger $monolog) { $filename = storage_path('/logs/laravel.log'); $handler = new Monolog\Handler\RotatingFileHandler($filename, 0, \Monolog\Logger::DEBUG, true, 0664); $handler->setFormatter(new \Monolog\Formatter\LineFormatter(null, null, true, true)); $monolog->pushHandler($handler); }); 

Il est également possible de combiner cela avec la réponse acceptée

 $app->configureMonologUsing(function(Monolog\Logger $monolog) { $filename = storage_path('/logs/laravel-' . php_sapi_name() . '.log'); $handler = new Monolog\Handler\RotatingFileHandler($filename, 0, \Monolog\Logger::DEBUG, true, 0664); $handler->setFormatter(new \Monolog\Formatter\LineFormatter(null, null, true, true)); $monolog->pushHandler($handler); }); 

Les versions récentes de Laravel 5.6 prennent en charge un élément d’ permission dans la configuration du pilote single et du pilote daily :

  'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', 'days' => 7, 'permission' => 0664, ], 

Pas besoin de jongler avec Monolog dans le script bootstrap.

Plus précisément, le support a été ajouté dans https://github.com/laravel/framework/commit/4d31633dca9594c9121afbbaa0190210de28fed8 .

Ajoutez quelque chose comme ceci au début de votre fichier app/start/artisan.php (c’est avec Laravel 4):

 // If effectively root, touch the log file and make sure it belongs to www-data if (posix_geteuid() === 0) { $file = storage_path() . '/logs/laravel.log'; touch($file); chown($file, 'www-data'); chgrp($file, 'www-data'); chmod($file, 0664); } 

Ajustez le chemin si le fichier journal quotidien que vous mentionnez n’est pas le fichier journal Laravel standard. Vous pouvez également ne pas vouloir changer le groupe ou définir les permissions comme je le fais ici. Ce qui précède définit le groupe sur www-data et définit les permissions d’écriture de groupe. J’ai ensuite ajouté mon utilisateur habituel au groupe www-data afin que l’exécution des commandes d’artisan en tant qu’utilisateur habituel puisse encore écrire dans le journal.

Un tweak lié est de mettre le suivant au début de votre fichier app/start/global.php :

 umask(0002); 

Si vous faites cela, la ligne chmod ci-dessus devient sans object. Avec le paramètre umask défini sur ceci, tous les nouveaux fichiers que PHP (et donc Laravel) ont créés auront leurs permissions masquées pour que les “autres” utilisateurs ne disposent pas des permissions d’écriture. Cela signifie que les répertoires commenceront par rwxrwxr-x et les fichiers par rw-rw-r-- . Donc, si www-data exécute PHP, tous les fichiers de cache et de journal qu’il crée seront accessibles en écriture par défaut pour toute personne du groupe principal de cet utilisateur, à savoir www-data .

Laravel 5.5

Ajoutez ce code à bootstrap/app.php :

 $app->configureMonologUsing(function (Monolog\Logger $monolog) { $filename = storage_path('logs/' . php_sapi_name() . '-' . posix_getpwuid(posix_geteuid())['name'] . '.log'); $monolog->pushHandler($handler = new Monolog\Handler\RotatingFileHandler($filename, 30)); $handler->setFilenameFormat('laravel-{date}-{filename}', 'Ym-d'); $formatter = new \Monolog\Formatter\LineFormatter(null, null, true, true); $formatter->includeStacktraces(); $handler->setFormatter($formatter); }); 
  • Il stockera des fichiers comme ceci: laravel-2018-01-27-cli-raph.log et laravel-2018-01-27-fpm-cgi-raph.log ce qui est plus lisible.
  • Les nouvelles lignes sont conservées (par défaut, comportement de Laravel)
  • Il fonctionne avec Laravel Log Viewer

Laravel 5.6

Vous devez créer une classe pour votre enregistreur:

 pushHandler($handler = new \Monolog\Handler\RotatingFileHandler($filename, 30)); $handler->setFilenameFormat('laravel-{date}-{filename}', 'Ym-d'); $formatter = new \Monolog\Formatter\LineFormatter(null, null, true, true); $formatter->includeStacktraces(); $handler->setFormatter($formatter); return $monolog; } } 

Ensuite, vous devez l’enregistrer dans config/logging.php :

 'channels' => [ 'custom' => [ 'driver' => 'custom', 'via' => App\Logging\CreateCustomLogger::class, ], ], 

Même comportement que pour 5.5:

  • Il stockera des fichiers comme ceci: laravel-2018-01-27-cli-raph.log et laravel-2018-01-27-fpm-cgi-raph.log ce qui est plus lisible.
  • Les nouvelles lignes sont conservées (par défaut, comportement de Laravel)
  • Il fonctionne avec Laravel Log Viewer

J’ai rencontré le même problème sur Laravel 5.6

Dans config/logging.php je viens de mettre à jour la valeur de chemin du canal quotidien avec php_sapi_name() .

Cela crée une durectory séparée pour différents php_sapi_name et place le fichier journal avec l’horodatage dans leur répertoire spécifique.

 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/' . php_sapi_name() . '/laravel.log'), 'level' => 'debug', 'days' => 7, ] 

Alors pour moi,

  • Les fichiers journaux sont créés dans le répertoire fpm-fcgi : Journaux du site Web, owner: www-data
  • Les fichiers journaux sont créés sous le répertoire cli : à partir de la commande artisan (cronjob). owner: root

Plus d’infos sur la journalisation de Laravel 5.6: https://laravel.com/docs/5.6/logging

Voici mon fichier config/logging.php :

  env('LOG_CHANNEL', 'stack'), /* |-------------------------------------------------------------------------- | Log Channels |-------------------------------------------------------------------------- | | Here you may configure the log channels for your application. Out of | the box, Laravel uses the Monolog PHP logging library. This gives | you a variety of powerful log handlers / formatters to utilize. | | Available Drivers: "single", "daily", "slack", "syslog", | "errorlog", "custom", "stack" | */ 'channels' => [ 'stack' => [ 'driver' => 'stack', 'channels' => ['daily'], ], 'single' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', ], 'daily' => [ 'driver' => 'daily', 'path' => storage_path('logs/' . php_sapi_name() . '/laravel.log'), 'level' => 'debug', 'days' => 7, ], 'slack' => [ 'driver' => 'slack', 'url' => env('LOG_SLACK_WEBHOOK_URL'), 'username' => 'Laravel Log', 'level' => 'critical', ], 'syslog' => [ 'driver' => 'syslog', 'level' => 'debug', ], 'errorlog' => [ 'driver' => 'errorlog', 'level' => 'debug', ], ], ]; 

Laravel 5,4

\Log::getMonolog()->popHandler(); \Log::useDailyFiles(storage_path('/logs/laravel-').get_current_user().'.log');

append à la fonction de boot dans AppServiceProvider

(Laravel 5.6) J’ai récemment rencontré le même problème et j’ai simplement défini une commande programmée pour s’exécuter dans /app/Console/Kernel.php .

$schedule->exec('chown -R www-data:www-data /var/www/**********/storage/logs')->everyMinute();

Je sais que c’est un peu exagéré, mais cela fonctionne comme un charme et n’a pas eu de problèmes depuis.

Une manière non-Laravel de faire ce travail est d’exécuter simplement votre cronjob en tant que www-data.

par exemple https://askubuntu.com/questions/189189/how-to-run-crontab-as-userwww-data

 /etc/crontab */5 * * * * www-data php /var/www/public/voto_m/artisan top >/dev/null 2>&1 

Le meilleur moyen que j’ai trouvé est que Fideloper suggère, http://fideloper.com/laravel-log-file-name , vous pouvez définir la configuration du journal laravel sans toucher à la classe Log. Avoir différents noms pour les programmes de console et les programmes HTTP, je pense, est la meilleure solution.