Tuesday 30 March 2021

Laravel: circumventing insecure default guidance about needing to have writeable files in the application code directory


Sorry, that was a bit of a mouthful. I'm having to look at Laravel for [reasons I won't go into here], and have recently waded through setting up a default web app with it.

I won't go into the installation process as I faffed around a fair bit and didn't really have any useful findings other than Laravel's whole approach to things seems a bit bloated. I mean it's just an MVC framework for webapps. Why does it need a special application to even install? Blimey. But anyway.

The thing I have found most horrifying about Laravel so far is this error message I received in the browser when I first cranked-up the application:

The stream or file "/usr/share/laravelExampleApp/storage/logs/laravel.log" could not be opened in append mode: Failed to open stream: Permission denied

Just to be clear: /usr/share/laravelExampleApp/ is my application directory. IE: where all the code is. Why the hell is Laravel trying to write to a log file in there? Nothing should ever be writing to an application directory.

Initially I thought I'd messed something up, but after a bunch of googling and reading stuff on Stack Overflow and issues in Github, I discovered that Laravel actually did this by "design". They actually expect the application to write temporary files to your application directory. They have this storage directory subtree in the root of the application:

adam@DESKTOP-QV1A45U:/mnt/c/src/laravel-example-app$ tree storage
├── app
│   └── public
├── framework
│   ├── cache
│   │   └── data
│   ├── sessions
│   ├── testing
│   └── views
└── logs

9 directories, 0 files

And it's for writing logs, cached shite, and other temporary files. What?

In addition to this, there's a second subdirectory structure that also needs to be writable, within one of the code subdirectories: <appDir>/bootstrap/cache. the <appDir>/bootstrap directory has application code in it, btw.

Well: we're not having that. Temporary files can go in the /var/tmp directory where they belong.

Now this is why I'm writing this article: it took me quite a while to work out how to change this: as far as I can tell none of it is documented, one just needs to wade through the code. So just in case someone (/ everyone ~) else has this issue, here's what to do.

Dealing with the bootstrap/cache stuff is super easy; hardly even an inconvenience. There's a bunch of environment variables Laravel pays attention to that define where to write temp files that by default go into bootstrap/cache. I've just chucked this lot into my .env file:

# The APP_STORAGE_PATH value below is for reference only.
# It can't be set here because it's needed in bootstrap/app.php before this file is loaded
# It needs to be set in the environment


(.env itself is not in source control, but it's based on this: .env.example).

Each of those environment variables define where to write their relevant file.

That comment about APP_STORAGE_PATH is part of the solution for relocating the storage directory. I've tried to follow the same approach as relocating the bootstrap/cache files, and had partial success, but unfortunately Laravel needs to know where that directory is before it's read its .env file. Of course it does. However the fix is a one-liner in bootstrap/app.php

$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)

$app->useStoragePath( env( 'APP_STORAGE_PATH', base_path() . '/storage' ) );

Having done that, Laravel is not tempted to try to write files to my application directory any more. Now I know that I should try to segregate out caching stuff from logging stuff from session stuff from random-other-shite stuff that Laravel mungs together in that storage directory, and I might get to that later if I need to care more about it; but for now at least it's out of the application directory.

Anyway. That's that. I needed something written down to point someone to, and this is it, and this is sufficient.